Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Assume we want to shift an array like it is done in the 2048 game: if we have two equal consecutive elements in array, merge them into twice the value element. Shift must return a new array, where every pair of consecutive equal elements is replaced with their sum, and pairs should not intersect. Shifting is performed only once, so we don't need to merge resulting values again. Notice that if we have 3 consecutive equal elements, we have to sum rightmost ones, so for example, [2, 2, 2] should become [2, 4], not [4, 2].

The task is to write shortest function which takes an array and returns a shifted array.

You may assume that all integers will be strictly positive.

Examples:

[] -> []
[2, 2, 4, 4] -> [4, 8]
[2, 2, 2, 4, 4, 8] -> [2, 4, 8, 8]
[2, 2, 2, 2] -> [4, 4]
[4, 4, 2, 8, 8, 2] -> [8, 2, 16, 2]
[1024, 1024, 512, 512, 256, 256] -> [2048, 1024, 512]
[3, 3, 3, 1, 1, 7, 5, 5, 5, 5] -> [3, 6, 2, 7, 10, 10]

I am also very interested in solution using reduce :)

share|improve this question
5  
This is a very nice first challenge. Welcome to the site! – DJMcMayhem 2 days ago
1  
Input is not necessarily sorted and numbers are greater than zero, that is the only restriction on numbers. We may let largest value fit in standard int32 bounds i think. Empty array gives empty array as a result. Thanks for the participation, appreciate that :) – greenwolf 2 days ago
2  
To those still voting to close as unclear, the challenge essentially boils down to this: Assume you have an array of positive integers. Walk through it from end to start. If the current element is equal to the next, replace it with the sum of both and move to the element after the replacement, then perform this check again for that element and the next. Repeat until the beginning of the array is reached. – user2428118 2 days ago
1  
@Titus "Notice that if we have 3 consecutive equal elements, we have to sum rightmost ones, so for example, [2, 2, 2] should become [2, 4], not [4, 2]." – Martin Ender 2 days ago
1  
The ruling on empty arrays is unfortunate; it has invalidated a few answers, including my own. – Dennis 2 days ago

25 Answers 25

Jelly, 10 9 8 bytes

Œg+2/€UF

TryItOnline or run all test cases

How?

Œg+2/€UF - Main link: a                 e.g. [2,2,2,4,4,8]
Œg       - group runs of equal elements      [[2,2,2],[4,4],[8]]
   2/€   - pairwise reduce for each with
  +      -     addition                      [[4,2],[8],[8]]
      U  - reverse (vectorises)              [[2,4],[8],[8]]
       F - flatten list                      [2,4,8,8]
share|improve this answer

Haskell, 47 57 50 bytes

e#l|a:b<-l,e==a= -2*a:b|1<2=e:l
map abs.foldr(#)[]

Uses reduce (or fold as it is called in Haskell, here a right-fold foldr). Usage example: map abs.foldr(#)[] $ [2,2,2,4,4,8] -> [2,4,8,8].

Edit: +10 bytes to make it work for unsorted arrays, too. Merged numbers are inserted as negative values to prevent a second merge. They are corrected by a final map abs.

share|improve this answer
    
The trick with negatives is really nice! – xnor yesterday

Brain-Flak 158 96

{({}<>)<>}<>{(({}<>)<><(({})<<>({}<>)>)>)({}[{}]<(())>){((<{}{}>))}{}{{}(<({}{})>)}{}({}<>)<>}<>

Try it online!

Explanation:

1 Reverse the list (moving everything to the other stack, but that doesn't matter)

{({}<>)<>}<>
{        }   #keep moving numbers until you hit the 0s from an empty stack
 ({}<>)      #pop a number and push it on the other stack
       <>    #go back to the original stack
          <> #after everything has moved, switch stacks

2 Do steps 3-6 until there is nothing left on this stack:

{                                                                                         }

3 Duplicate the top two elements (2 3 -> 2 3 2 3)

(({}<>)<><(({})<<>({}<>)>)>)

(({}<>)<>                   #put the top number on the other stack and back on the very top
         <(({})             #put the next number on top after:
               <<>({}<>)>   #copying the original top number back to the first stack
                         )>)

4 Put a 1 on top if the top two are equal, a 0 otherwise (from the wiki)

({}[{}]<(())>){((<{}{}>))}{}

5 If the top two were equal (non-zero on the top) add the next two and push the result

{{}(<({}{})>)}{}
{            }   #skip this if there is a 0 on top
 {}              #pop the 1
   (<      >)    #push a 0 after:
     ({}{})      #pop 2 numbers, add them together and push them back on 
              {} #pop off the 0

6 Move the top element to the other stack

({}<>)<>

7 Switch to the other stack and print implicitly

<>
share|improve this answer

PHP, 116 Bytes

<?$r=[];for($c=count($a=$_GET[a]);$c-=$x;)array_unshift($r,(1+($x=$a[--$c]==$a[$c-1]))*$a[$c]);echo json_encode($r);

or

<?$r=[];for($c=count($a=$_GET[a]);$c--;)$r[]=$a[$c]==$a[$c-1]?2*$a[$c--]:$a[$c];echo json_encode(array_reverse($r));

-4 Bytes if the output can be an array print_r instead of 'json_encode`

176 Bytes to solve this with a Regex

echo preg_replace_callback("#(\d+)(,\\1)+#",function($m){if(($c=substr_count($m[0],$m[1]))%2)$r=$m[1];$r.=str_repeat(",".$m[1]*2,$c/2);return trim($r,",");},join(",",$_GET[a]));
share|improve this answer
1  
You cannot use sort as the result is not always sorted : [4, 4, 2, 8, 8, 2] -> [8, 2, 16, 2] – Crypto 2 days ago
    
@Crypto You are right after the new test cases has been added. Before the use of sort was okay – Jörg Hülsermann 2 days ago
    
for($i=count($a=$argv);--$i;)$b[]=($a[$i]==$a[$i-1])?2*$a[$i‌​--]:$a[$i];print_r(a‌​rray_reverse($b)); same idea but shorter – Crypto 2 days ago
    
@Crypto I'm not sure about the output as string representation or an array. for the testcase [] I need $r=[]; Thank You for your help – Jörg Hülsermann 2 days ago

Perl, 43 + 1 (-p) = 44 bytes

Ton Hospel came up with 41 bytes answer, check it out!

-4 thanks to @Ton Hospel!

Edit : added \b, as without it it was failing on input like 24 4 on which the output would have been 28.

$_=reverse reverse=~s/(\b\d+) \1\b/$1*2/rge

Run with -p flag :

perl -pe '$_=reverse reverse=~s/(\b\d+) \1\b/$1*2/rge' <<< "2 2 2 4 4"


I don't see another way than using reverse twice to right-fold (as just s/(\d+) \1/$1*2/ge would left-fold, i.e 2 2 2 would become 4 2 instead of 2 4). So 14 bytes lost thanks to reverse... Still I think there must be another (better) way (it's perl after all!), let me know if you find it!

share|improve this answer
    
reverse reverse seems a bit lengthy. I'm no expert in Perl, but is there a way you can make a shortcut to reverse (if nothing else, [ab]using eval)? – Cyoce 2 days ago
    
Nice sexeger. Notice you can just leave out the ($_) – Ton Hospel 2 days ago
    
@TonHospel thanks. Indeed, the doc of reverse looks like reverse can't be called without argument (well the examples show it can be, but there is only one prototype : reverse LIST), so I forgot about $_ being the default argument ;) – Dada 2 days ago
    
A LIST can be empty... – Ton Hospel 2 days ago
    
@TonHospel indeed, but usually when an operator uses $_ as default argument, the doc specifies a prototype with no parameters (like print or lenght...). Or maybe that's just an wrong impression I have. – Dada 2 days ago

Python, 61 bytes

def f(l):b=l[-2:-1]==l[-1:];return l and f(l[:~b])+[l[-1]<<b]

The Boolean b checks whether the last two elements should collapse by checking that they are equal in a way that's safe for lists of length 1 or 0. The last element if then appended with a multiplier of 1 for equal or 2 for unequal. It's appended to the recursive result on the list with that many elements chopped off the end. Thanks to Dennis for 1 byte!

share|improve this answer
    
[l[-1]<<b] saves a byte. – Dennis 2 days ago
    
l[-2:-1] is [l[-2]] – mbomb007 yesterday
1  
I need it to work for lists of size 0 and 1. – xnor yesterday

Perl 5.10, 61 50 bytes (49 + 1 for flag)

Thanks to Ton Hospel for saving 11 bytes!

Regex-free solution, with -a flag:

@a=($F[-1]-$b?$b:2*pop@F,@a)while$b=pop@F;say"@a"

Try here!

share|improve this answer
    
Nice alternative method. A pity arrays almost always lose to strings in perl. Still, you can get a bit closer by golfing your code to @a=($F[-1]-$b?$b:2*pop@F,@a)while$b=pop@F;say"@a" (50 bytes) – Ton Hospel 2 days ago
    
@TonHospel Indeed, I tend to avoid string-based solutions (just to show that Perl can do more than that!). I don't play to win anyway :D Thanks for the golfing tips! – Paul Picard yesterday

GNU sed 41 38 37

Includes +1 for -r
-3 Thanks to Digital Trauma
-1 Thanks to seshoumara

:
s,(.*)(1+) \2\b,\1!\2\2!,
t
y,!, ,

Input and output are space separated strings in unary (based on this consensus).

Try it online!

share|improve this answer
    
@DigitalTrauma Yep! I always forget about that. Thanks! – Riley 2 days ago
    
Use y,!, , to save 1 byte. – seshoumara 16 hours ago
    
@seshoumara Duh... Why didn't I think of that. Thanks! – Riley 16 hours ago

Retina, 32

\d+
$*
r`\b\1 (1+)\b
$1$1
1+
$.&

r on line 3 activates right-to-left regex matching. And this means that the \1 reference needs to come before the (1+) capturing group that it references.

Try it online.

share|improve this answer
    
Nice.. That right-to-left option to match is quite handy! Is it part of .Net regex or a Retina feature? – Dada 2 days ago
    
I was just about to post mine at 26, using linefeed-separation as the input format: retina.tryitonline.net/… the main savings come from that and using transliteration to get rid of the second substitution. – Martin Ender 2 days ago
    
@Dada It's a .NET feature (and it's used under the hood to enable arbitrary-length lookbehinds). Retina has no unique regex features yet (although it has some unique substitution features). – Martin Ender 2 days ago
    
@MartinEnder Ok thanks! .NET regex's are really great! jealous perl coder spotted – Dada 2 days ago
    
@MartinEnder I your solution is different enough to warrant another answer – Digital Trauma 2 days ago

Perl, 41 bytes

Includes +1 for -p

Give input sequence on STDIN:

shift2048.pl <<< "2 2 2 4 4 8 2"

shift2048.pl:

#!/usr/bin/perl -p
s/.*\K\b(\d+) \1\b/2*$1.A/e&&redo;y/A//d
share|improve this answer

05AB1E, 26 bytes

D¥__X¸«DgL*ê¥X¸«£vy2ôO})í˜

Try it online!

Generalized steps

  1. Reduce by subtraction to find where consecutive elements differ
  2. Reduce by subtraction over the indices of those places to find the length of consecutive elements
  3. Split input into chunks of those lengths
  4. Split chunks into pairs
  5. Sum each pair
  6. Reverse each summed chunk
  7. Flatten to 1-dimensional list
share|improve this answer

JavaScript (ES6), 68 65 58 57 65 bytes

Fixed for unsorted arrays now that it has been clarified that such inputs are to be expected.

f=(a,l=[],m=1)=>(x=a.pop())*m-l?f(a,x).concat(l):x?f(a,2*x,0):[l]

console.log(f([2, 2, 4, 4]));
console.log(f([2, 2, 2, 4, 4, 8]));
console.log(f([2, 2, 2, 2]));
console.log(f([4, 2, 2]));

Alternate version, 72 bytes

A non-recursive version using a regular expression:

a=>a.reverse().join`+`.replace(/(.)\+\1/g,c=>eval(c)).split`+`.reverse()
share|improve this answer
1  
I was about to suggest the edit you just made :) – ETHproductions 2 days ago

JavaScript (ES6), 68 bytes

f=a=>a.reduceRight((p,c)=>(t=p[0],p.splice(0,c==t,c==t?c+t:c),p),[])
    
console.log([
  [],
  [2, 2, 4, 4],
  [2, 2, 2, 4, 4, 8],
  [2, 2, 2, 2],
  [4, 4, 2, 8, 8, 2],
  [1024, 1024, 512, 512, 256, 256],
  [3, 3, 3, 1, 1, 7, 5, 5, 5, 5],
].map(f))

share|improve this answer
    
Not bad, but according to the executed snippet: [1024, 1024, 512, 512, 256, 256] is resolving as [2048, 512, 1024] and not [2048, 1024, 512]...? – WallyWest yesterday

Mathematica, 53 bytes

Join@@(Reverse[Plus@@@#~Partition~UpTo@2]&/@Split@#)&

Explanation

Split@#

Split the input into sublists consisting of runs of identical elements. i.e. {2, 2, 2, 4, 8, 8} becomes {{2, 2, 2}, {4}, {8, 8}}.

#~Partition~UpTo@2

Partition each of the sublist into partitions length at most 2. i.e. {{2, 2, 2}, {4}, {8, 8}} becomes {{{2, 2}, {2}}, {{4}}, {{8, 8}}}.

Plus@@@

Total each partition. i.e. {{{2, 2}, {2}}, {{4}}, {{8, 8}}} becomes {{4, 2}, {4}, {16}}.

Reverse

Reverse the results because Mathematica's Partition command goes from left to right, but we want the partitions to be in other direction. i.e. {{4, 2}, {4}, {16}} becomes {{2, 4}, {4}, {16}}.

Join@@

Flatten the result. i.e. {{2, 4}, {4}, {16}} becomes {2, 4, 4, 16}.

share|improve this answer
    
Hi JHM! Thanks for the answer. I don't understand Mathematica very well, so could you add a bit of explanation as to what's going on? – isaacg 2 days ago
    
Plus@@@ is Tr/@ and I think you can avoid the parentheses and Join@@ if you use ##&@@ on the result of Reverse (haven't tested it though). – Martin Ender 2 days ago

Perl, 37 bytes

Includes +4 for -0n

Run with the input as separate lines on STDIN:

perl -M5.010 shift2048.pl
2
2
2
4
4
8
2
^D

shift2048.pl:

#!/usr/bin/perl -0n
s/\b(\d+
)(\1|)$//&&do$0|say$1+$2
share|improve this answer

Haskell, 56 bytes

g(a:b:r)|a==b=a+b:g r|l<-b:r=a:g l
g x=x
r=reverse
r.g.r
share|improve this answer

PHP, 86 100 99 bytes

that should do it:

<?$p=array_pop;for($r=[];$v=$p($a=&$_GET[a]);)array_unshift($r,end($a)-$v?$v:2*$p($a));print_r($r);

call in browser with ?a[]=value1&a[]=value2...

share|improve this answer
1  
[2, 2, 2] returns [4,2] instead of [2,4] – Crypto 2 days ago
    
@Crypto: got it. thanks. – Titus 2 days ago
    
for($r=[];$v=($p=array_pop)($a=&$_GET[a]);)array_unshift($r,‌​end($a)-$v?$v:2*$p($‌​a));print_r($r); is 1 Byte shorter – Jörg Hülsermann 2 days ago

Java 7, 133 bytes

Object f(java.util.ArrayList<Long>a){for(int i=a.size();i-->1;)if(a.get(i)==a.get(i-1)){a.remove(i--);a.set(i,a.get(i)*2);}return a;}

Input is an ArrayList, and it just loops backwards, removing and doubling where necessary.

Object f(java.util.ArrayList<Long>a){
    for(int i=a.size();i-->1;)
        if(a.get(i)==a.get(i-1)){
            a.remove(i--);
            a.set(i,a.get(i)*2);
        }
    return a;
}
share|improve this answer

Julia 205 bytes

t(x)=Val{x}
s(x)=t(x)()
f^::t(1)=f
^{y}(f,::t(y))=x->f(((f^s(y-1))(x)))
g()=[]
g{a}(::t(a))=[a]
g{a}(::t(a),B...)=[a;g(B...)]
g{a}(::t(a),::t(a),B...)=[2a;g(B...)]
K(A)=g(s.(A)...)
H(A)=(K^s(length(A)))(A)

Function to be called is H

eg H([1,2,2,4,8,2,])

This is in no way the shortest way do this in julia. But it is so cool, that I wanted to share it anyway.

  • t(a) is a value-type, representing the value (a).
  • s(a) is an instance of that value type
  • g is a function that dispatches on the difference values (using the value types) and numbers of its parameters. And that is cool
  • K just wraps g so that

Extra cool part:

f^::t(1)=f
^{y}(f,::t(y))=x->f(((f^s(y-1))(x)))

This defines the ^ operator to apply to functions. So that K^s(2)(X) is same as K(K(X)) so H is just calling K on K a bunch of times -- enough times to certainly collapse any nested case

This can be done much much shorter, but this way is just so fun.

share|improve this answer

Python 2, 94 bytes

def f(a,r=[]):
 while a:
    if len(a)>1and a[-1]==a[-2]:a.pop();a[-1]*=2
    r=[a.pop()]+r
 print r

Try it online

share|improve this answer

PowerShell v2+, 81 bytes

param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count..0]

Takes input as an explicit array $n, reverses it $n[$n.count..0], -joins the elements together with a comma, then regex -replaces a matching digit pair with the first element, a *2, and surrounded in parens. Pipes that result (which for input @(2,2,4,4) will look like (4*2),(2*2)) over to iex (short for Invoke-Expression and similar to eval), which converts the multiplication into actual numbers. Stores the resulting array into $b, encapsulates that in parens to place it on the pipeline, then reverses $b with [$b.count..0]. Leaves the resulting elements on the pipeline, and output is implicit.


Test Cases

NB-- In PowerShell, the concept of "returning" an empty array is meaningless -- it's converted to $null as soon as it leaves scope -- and so it is the equivalent of returning nothing, which is what is done here in the first example (after some wickedly verbose errors). Additionally, the output here is space-separated, as that's the default separator for stringified arrays.

PS C:\Tools\Scripts\golfing> @(),@(2,2,4,4),@(2,2,2,4,4,8),@(2,2,2,2),@(4,4,2,8,8,2),@(1024,1024,512,512,256,256),@(3,3,3,1,1,7,5,5,5,5)|%{"$_ --> "+(.\2048-like-array-shift.ps1 $_)}
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At C:\Tools\Scripts\golfing\2048-like-array-shift.ps1:7 char:67
+   param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count. ...
+                                                                   ~~~
    + CategoryInfo          : InvalidData: (:String) [Invoke-Expression], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand

Cannot index into a null array.
At C:\Tools\Scripts\golfing\2048-like-array-shift.ps1:7 char:13
+   param($n)($b=$n[$n.count..0]-join','-replace'(\d+),\1','($1*2)'|iex)[$b.count. ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

 --> 
2 2 4 4 --> 4 8
2 2 2 4 4 8 --> 2 4 8 8
2 2 2 2 --> 4 4
4 4 2 8 8 2 --> 8 2 16 2
1024 1024 512 512 256 256 --> 2048 1024 512
3 3 3 1 1 7 5 5 5 5 --> 3 6 2 7 10 10
share|improve this answer

NodeJS - 57 53 bytes

b=a=>{a.reduceRight((z,e)=>{return z+e;});return a;}
share|improve this answer
    
This doesn't seem to work, b([1, 1]) gives [1, 1], whereas it should give [2]. – Loovjo yesterday
    
I'm running it with node. Do you think that can make a difference ? (this input works for me) – Alexis_A yesterday
    
It does make a difference, it doesn't work on chrome's console. – Alexis_A yesterday

Racket 170 bytes

(λ(l)(let g((l(reverse l))(o '()))(cond[(null? l)o][(=(length l)1)(cons(car l)o)]
[(=(car l)(list-ref l 1))(g(drop l 2)(cons(* 2(car l))o))][(g(cdr l)(cons(car l)o))])))

Ungolfed:

(define f1
  (λ (lst)
    (let loop ((lst (reverse lst)) (ol '()))
      (cond
        [(null? lst) ol]
        [(= (length lst) 1)
           (cons (first lst) ol)]
        [(= (first lst) (list-ref lst 1))
           (loop (drop lst 2) (cons (* 2 (first lst)) ol))]
        [(loop (rest lst) (cons (first lst) ol))]
        ))))

Testing:

(f '[])
(f '[2 2 4 4]) 
(f '[2 2 2 4 4 8]) 
(f '[2 2 2 2]) 
(f '[4 4 2 8 8 2])
(f '[1024 1024 512 512 256 256]) 
(f '[3 3 3 1 1 7 5 5 5 5])
(f '[3 3 3 1 1 7 5 5 5 5 5])

Output:

'()
'(4 8)
'(2 4 8 8)
'(4 4)
'(8 2 16 2)
'(2048 1024 512)
'(3 6 2 7 10 10)
'(3 6 2 7 5 10 10)
share|improve this answer

Julia, 73 82 Bytes

f(l)=l==[]?[]:foldr((x,y)->y[]==x?vcat(2x,y[2:end]):vcat(x,y),[l[end]],l[1:end-1])

Use right fold to build list from back to front (one could also use fold left and reverse the list at the beginning and end).

If the head of the current list is not equal to the next element to prepend, then just prepend it.

Else remove the head of the list (sounds kind of cruel) and prepend the element times 2.

Example

f([3,3,3,1,1,7,5,5,5,5]) 
returns a new list:
[3,6,2,7,10,10]
share|improve this answer

Java 8, 130 114 bytes

Edit:

  • -16 bytes off. Thanks to @Kevin Cruijssen.

Snipet:

s->{int j=0,i=s.length,l[]=new int[i];for(;--i>-1;[j++]=s[i]==s[i-1]?s[i--]*2:s[i]);return Collection.reverse(l);}

Code:

public static int[] shift(int[]a){
  List<int> out = new ArrayList<>();
  for(int i = s.length-1; i>-1;i--){
    if(s[i]==s[i-1]){
      out.add(s[i]*2);
      i--;
    }else{
      out.add(s[i]);
    }
  }
  return Arrays.reverse(out.toArray());
}
share|improve this answer
1  
Hi, you can golf it some more like this: s->{int j=0,i=s.length,l[]=new int[i];for(;--i>-1;[j++]=s[i]==s[i-1]?s[i--]*2:s[i]);...} Also, Arrays#reverse doesn't exist?.. :S Collections#reverse does, but Arrays not a.f.a.i.k. (And if it did exist, you'd need java.util. in front of it.) – Kevin Cruijssen 2 days ago
1  
This code is not fully representational of the full code required to run it, nor is it capable of running as-is. – VTCAKAVSMoACE 2 days ago
    
I wrote Snipet not full programm and this declares a function object as the OP said: The task is to write a function [...] – Roman Gräf 2 days ago
    
I can't get either version of your code to compile in Java 8. Additionally, it looks to me like your golfed code returns an array of the original size, not the newly modified size. – Geobits yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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