17
\$\begingroup\$

While at work I spotted a nice and simple challenge.

The job was to stock products in cargo units with a certain capability. Since an order can have various batches, coming in sequence from the production, we usually make a list of the pieces of those batches distributed in each cargo to make sure nothing is lost during the process.

For example if we had this production sheet:

 _____________
|ord: xxx     |
|_____________|
| A | 30 pcs  |
| B | 38 pcs  |
| C | 12 pcs  |
 —————————————

and our cargo could contain 12 pieces we would make this list:

A | 12 12  6-
B | -6 12 12  8-
C | -4  8//

where x- indicates that x pieces will be stocked with -y pieces of the next line and z// indicates that after those z pieces there's a different order, thus nothing more we can add, and the last cargo is complete.

Task

In this challenge you have to take a production sheet and a cargo capacity and output the distributed list.

Specifications

Production sheet is taken in the form of a sequence of numbers representing the batches amounts.

You have to output just the distributed amounts, we don't care about the additional signs - // mentioned above.

Every batch must be distributed separately, so the output is a list of lists which you can represent in any reasonable format, just specify it and be consistent.

It's guarantee that:

  • each batch has at least one piece
  • cargo units can contain at least one piece
  • an order( input list ) has at least one batch

So you have to handle only positive integers( >0 ).

input:

a list of numbers representing the production sheet and a number representing the cargo capacity, each positive integers, in any reasonable method / order.

output:

the distributed list in form of a list of lists of numbers in any convenient method.

  • No extraneous numbers can appear in the output.
  • Order of obtained lists must be preserved.

Test cases

 [30, 38, 12], 12 => [[12,12,6],[6,12,12,8],[4,8]]
 [1],1 => [[1]]
 [2],1 => [[1,1]]
 [1],2 => [[1]]
 [1,12,34],20 => [[1],[12],[7,20,7]]
 [5],1 => [[1,1,1,1,1]]
 [5],5 => [[5]]
 [300,120],84 => [[84,84,84,48],[36,84]]
 [1,2,3,4,5],6 => [[1],[2],[3],[4],[2,3]]
 [2,5,16,38,32,38,38,34,8,30,31,9,12],15 => [[2],[5],[8,8],[7,15,15,1],[14,15,3],[12,15,11],[4,15,15,4],[11,15,8],[7,1],[14,15,1],[14,15,2],[9],[4,8]]

Rules

  • Input/output can be given by any convenient method.
  • You can print it to STDOUT, return it as a function result or error message/s.
  • Either a full program or a function are acceptable.
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

Sandbox

\$\endgroup\$
2
  • \$\begingroup\$ I thought this was a question about implementing dpkg or something horrifying \$\endgroup\$
    – qwr
    Feb 21, 2021 at 20:35
  • \$\begingroup\$ @qwr I never heard of dpkg before, just opened wiki and got scared, this is just a simple counting problem we solve everyday at work by hand. I thought it was a simple and relaxing challenge but with so few answer it was probably more difficult to solve by code \$\endgroup\$
    – AZTECCO
    Feb 21, 2021 at 22:09

12 Answers 12

8
\$\begingroup\$

J, 36 34 27 24 bytes

(]<@(#/.~)/.[:I.#\&:-)I.

Try it online!

-3 bytes thanks to Bubbler

Takes capacity as left arg and production sheet as right arg.

how

Use 12 f 30 38 12 as an example.

  • (...)I. A dyadic hook which expands the right arg into a mask with 30 0s, 38 1s, and 12 2s:

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2
    
  • #\&:- makes both arguments negative &:-. The _12 on the left means "chunks of size 12" and #\ will count each:

     12 12 12 12 12 12 8
    
  • [:I. Use the same expansion trick we used before to expand each of those in place into a unique integer:

     0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6
    
  • ].../. Use the mask from step 1 to group the output of the last step into groups of size 30, 38, and 12:

      0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 
      2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5
      5 5 5 5 6 6 6 6 6 6 6 6
    
  • (#/.~)/. For each of those groups, self-group by value, returning a list of the number of items in each group:

      _________ 12 ________   ________ 12 _________   ___ 6 ___ 
     /                     \ /                     \ /         \
     0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 
    
      ___ 6 ___   ________ 12 _________   ________ 12 _________   _____ 8 _____
     /         \ /                     \ /                     \ /             \
     2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5
    
      _ 4 _   _____ 8 _____
     /     \ /             \
     5 5 5 5 6 6 6 6 6 6 6 6
    
  • Finally, box each of those results <@ because J doesn't allow ragged arrays.

\$\endgroup\$
3
  • \$\begingroup\$ -1: (<.@%~[:i.+/) -> I.@(#\&:-I.) \$\endgroup\$
    – Bubbler
    Feb 19, 2021 at 1:47
  • 1
    \$\begingroup\$ Then factor out the monadic I. on the right arg to get 24 bytes. \$\endgroup\$
    – Bubbler
    Feb 19, 2021 at 1:55
  • \$\begingroup\$ @Bubbler Those are some lovely insights. Thanks! \$\endgroup\$
    – Jonah
    Feb 19, 2021 at 2:01
6
\$\begingroup\$

Stax, 19 bytes

ÇÑ▄≥Ué(ΘⁿGD$▀X9♦;ûΘ

Run and debug it

Explanation:

Unpacked source:

{i]*m$s/{|Rm:f{h}/mMHJ
  • Implicit input: 12 [30 38 12]
  • {i]*m: Map over array:
    • i]: Push singleton array with index, e.g. [1]
    • *: Repeat by corresponding element, e.g. [1 1 1 1 1 ... (38 times)]
  • Result: [[<30 times 0>] [<38 times 1>] [<12 times 2>]]
  • $: Flatten: [<30 times 0> <38 times 1> <12 times 2>]
  • s/: Split into groups of maximum package size: [[<12 times 0>] [<12 times 0>] [<6 times 0> <6 times 1>] [<12 times 1>] [<12 times 1>] [<8 times 1> <4 times 2>] [<8 times 2>]
  • {|Rm: Run-length-encode each element: [[[0 12]] [[0 12]] [[0 6] [1 6]] [[1 12]] [[1 12]] [[1 8] [2 4]] [[2 8]]]
  • :f: Flatten: [[0 12] [0 12] [0 6] [1 6] [1 12] [1 12] [1 8] [2 4] [2 8]]
  • {h}/: Split into groups with equal first element: [[[0 12] [0 12] [0 6]] [[1 6] [1 12] [1 12] [1 8]] [[2 4] [2 8]]]
  • m: For each element, e.g. [[0 12] [0 12] [0 6]]
    • M: Transpose: [[0 0 0] [12 12 6]]
    • H: Second element: [12 12 6]
    • J: Join with spaces: "12 12 6"
    • Implicit print
\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES6), 57 bytes

Expects (capacity)(list).

n=>a=>a.map(g=v=>v?[x=v>m?m:v,...g(v-x,m=m-x||n)]:[],m=n)

Try it online!

Commented

n => a =>              // n = cargo capacity, a[] = production list
  a.map(g = v =>       // for each quantity v in a[]:
    v ?                //   if v is not equal to 0:
      [                //     build a new array:
        x = v > m ? m  //       append x = min(m, v)
                  : v, //       
        ...g(          //       split the result of a recursive call:
          v - x,       //         subtract x from v
          m = m - x    //         subtract x from m; if the result is 0:
              || n     //           restart with a new cargo of capacity n
        )              //       end of recursive call
      ]                //     end of array
    :                  //   else:
      [],              //     stop the recursion
    m = n              //   start with m = n
  )                    // end of map()
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for helping doing my job so fast! \$\endgroup\$
    – AZTECCO
    Feb 18, 2021 at 20:53
5
\$\begingroup\$

Haskell, 67 66 bytes

1 byte saved by xnor

f c|let(a:d)?b|a<=b=[a]:d?(b-a)|x:y<-(a-b:d)?c=(b:x):y;_?_=[]=(?c)

Try it online!

I think this takes the cake as the most unreadable Haskell I have written. We have a pattern guard inside a pattern guard which I am actually surprised Haskell can even parse.

Here it is ungolfed:

f c
  | let
    (a:d)?b
      | a <= b
        = [a]:d?(b-a)
      | x:y <- (a-b:d)?c
        = (b:x):y
    _?_
     = []
  = (?c)
\$\endgroup\$
6
  • \$\begingroup\$ Wow! That's crazy! I don't understand how the second sub guard works x:y <- c?(a-b:d) = .., there's no condition 0.o \$\endgroup\$
    – AZTECCO
    Feb 19, 2021 at 19:10
  • \$\begingroup\$ @AZTECCO The condition there is a pattern match. If c?(a-b:d) did not match x:y (that is it is an empty list) it would fail. That being said more generally you can use pattern matches in guards even when they can't fail e.g. x <- m+3 basically acts as an assignment. \$\endgroup\$
    – Wheat Wizard
    Feb 19, 2021 at 19:19
  • \$\begingroup\$ ♦ thanks! Ok so it does two things simultaneously : check if a?b produce a list(which is a guard) and assigns it to t x:y which is then used in the result if I understood, but where's the edge case ? Is it _?_ ? But it's outside the guards.. Omg! \$\endgroup\$
    – AZTECCO
    Feb 19, 2021 at 21:28
  • 1
    \$\begingroup\$ _?_ is a catch all, it is performed if nothing else matches. It's the base case it only ever gets called when the list is empty. \$\endgroup\$
    – Wheat Wizard
    Feb 19, 2021 at 22:05
  • \$\begingroup\$ Can you swap the two sides of ? to cut the space? \$\endgroup\$
    – xnor
    Feb 20, 2021 at 12:42
4
\$\begingroup\$

05AB1E, 9 bytes

OÝI÷¹£εÅγ

Try it online or verify all test cases.

Explanation:

O          # Sum the first (implicit) input-list
 Ý         # Push a list in the range [0,sum]
  I÷       # Integer-divide each by the second input-integer
    ¹£     # Split it into groups of the first input-list amount of values
      ε    # Map over each group:
       Åγ  #  And run-length encode it, which will put the values and counts as two
           #  separated lists to the stack, and since we're using a large map `ε`, it
           #  will only leave the top one with the counts
           # (after which the result is output implicitly)
\$\endgroup\$
3
\$\begingroup\$

Charcoal, 31 bytes

UMθ…Eι⊞Oυκ⁰F⪪υηF…·⌊ι⌈ι⊞§θκ№ικIθ

Try it online! Link is to verbose version of code. Outputs each entry on its own line with batches double-spaced. Explanation:

UMθ…Eι⊞Oυκ⁰

For each element of the input array, push its index n times to the predefined empty list, and then replace it with an empty array, thereby creating a master batch list.

F⪪υη

Split the list of indices into batches of the given capacity and loop over each batch.

F…·⌊ι⌈ι

Loop over each value in the current batch.

⊞§θκ№ικ

Push the value's frequency to the master batch list.

Iθ

Print the resulting list.

\$\endgroup\$
2
\$\begingroup\$

R, 123 80 78 bytes

Edit: -43 bytes by stealing tricks from blatantly copying Kevin Cruijssen's 05AB1E answer (please upvote that one!)

function(s,c)sapply(lapply(split((1:sum(s)-1)%/%c,rep(seq(a=s),s)),rle),`[`,1)

Try it online!

\$\endgroup\$
2
\$\begingroup\$

C (clang), 93 75 bytes

  • Original answer setting the Bounty score required under 93.
p,u;f(*l,c,z){for(p=u=0;++u,++p,z;u*=u!=c)--*l&&u-c||printf("%d%c",p,!*l?++l,z--,10:32,p=0);}

Try it online!

  • 75 bytes after some golfing.
  • stolen from @Arnaulds the 0 terminated list input.
p,u;f(*l,c){for(p=u=1;*l||*++l;++p)u++%c*--*l||printf("%d%c",p,9+!*l,p=0);}

Try it online!

  • u is the total counter of pieces.
  • p is the partial counter.
  • c is cargo capacity
  • when u%c == 0 or current batch (*l) is 0 we print the partial and reset it.
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Doing it one unit at a time is a great idea! \$\endgroup\$
    – Arnauld
    Feb 22, 2021 at 13:15
1
\$\begingroup\$

Perl 5, 116 bytes

sub{$c=pop;map[map y///c,/x+|y+|X+|Y+/g],join('',map[X,Y]->[$n++%2]x$_,@_)=~s/(.{$c})(.{1,$c})/$1\L$2/gr=~/x+|y+/gi}

Try it online!

sub{
  $c=pop;                           #capacity from last input
  map [map y///c,/x+|y+|X+|Y+/g],   #split into same letters case-sensitive
                                    #...and convert into lengths as numbers
  join('',map[X,Y]->[$n++%2]x$_,@_) #stringify order into XXXYYYXX of given sizes
  =~ s/(.{$c})(.{1,$c})/$1\L$2/gr   #lower case every other capacity
  =~ /x+|y+/gi                      #split into same letters case-insensitive
}
\$\endgroup\$
1
\$\begingroup\$

Jelly, 9 bytes

SḶṁẋ@:Œɠ€

Try it online!

Port of @KevinCruijssen's 05AB1E answer.

Explanation

SḶṁẋ@:Œɠ€   Main dyadic link
S           Sum
 Ḷ          Lowered range [0..n-1]
  ṁ         Reshape like
   ẋ@         a list consisting of lists of the second argument repeated each first argument times
     :      Integer divide by the second argument
      Œɠ    Run lengths
        €     of each
\$\endgroup\$
1
+100
\$\begingroup\$

C (clang),  91 87 86  83 bytes

Saved 4 bytes by switching to clang and using TABs, as suggested by @AZTECCO
Saved 3 bytes thanks to @ceilingcat

Expects a zero-terminated list of quantities.

m,x;f(int*a,n){for(m=n;*a?printf("%d%c",x=*a>m?m:*a,(m=m-x?:n,*a-=x)?9:13):*++a;);}

Try it online!

\$\endgroup\$
5
  • \$\begingroup\$ Ah! Zero terminated.. I didn't considered it! Congrats @Arnauld .. You can leave int*x; by using clang instead of gcc \$\endgroup\$
    – AZTECCO
    Feb 20, 2021 at 22:10
  • \$\begingroup\$ And -1 by using horizontal tab (9) \$\endgroup\$
    – AZTECCO
    Feb 20, 2021 at 22:15
  • \$\begingroup\$ @AZTECCO Thanks! I've also fixed it to support empty lists -- not sure if that's required. \$\endgroup\$
    – Arnauld
    Feb 20, 2021 at 22:17
  • \$\begingroup\$ Nice to help and see a good C solution! For the empty list it's not required, it's guaranteed that lists have at least one batch as x specifications, 87 Bytes should be fine. Btw mine will go down to 87 too using 0 terminated list like yours and tab, and has a different approach! (bounty still yours atm) \$\endgroup\$
    – AZTECCO
    Feb 20, 2021 at 22:27
  • 1
    \$\begingroup\$ @AZTECCO Ah, ok. I missed that part of the spec. That's -1 byte, then. \$\endgroup\$
    – Arnauld
    Feb 20, 2021 at 22:34
1
\$\begingroup\$

Clojure, 111 bytes

#(let[p partition-by](for[k(p last(for[i(partition-all %2(mapcat repeat %1(range)))j(p + i)]j))](map count k)))

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.