7
\$\begingroup\$

The task is to group adjacent values from collection and output it as a new aggregated collection.

Consider we have following input:

Sequence
Number      Value
========    =====
1           9
2           9
3           15
4           15
5           15
6           30
7           9

The aim is to enumerate over Sequence Numbers and check if the next element has the same value. If yes, values are aggregated and so, desired output is as following:

Sequence    Sequence
Number      Number
From        To          Value
========    ========    =====
1           2           9
3           5           15
6           6           30
7           7           9

Build a code with following assumptions:

  • Sequence Numbers are already ordered in input table.
  • There can be gaps in Sequence Numbers. If input contains gaps, output should contain gaps too. For example input 1 99\n2 99\n4 99\n6 99, output will be 1 2 99\n4 4 99\n6 6 99.
  • Assume Value is type of integer.
  • It can be a full program or just a function.
  • This cruft (Sequence Sequence\n ... ======== ======== =====\n) is not a part of output. Output format is not defined; there should be a delimeter between values and between rows, so anyone can distinguish values and rows.

It's an extend of my StackOverflow question: https://stackoverflow.com/questions/14879197/linq-query-data-aggregation.

\$\endgroup\$
  • 1
    \$\begingroup\$ Should it be a full program with input/output or just a function? \$\endgroup\$ – grc Feb 28 '13 at 1:39
  • 1
    \$\begingroup\$ Is all that cruft at the top (Sequence Sequence\n ... ======== ======== =====\n) part of the expected input and output? If not, can you please specify the expected formats? \$\endgroup\$ – dmckee Feb 28 '13 at 1:50
  • \$\begingroup\$ @grc: I precised rules, thank you. \$\endgroup\$ – Dariusz Woźniak Feb 28 '13 at 9:50
  • \$\begingroup\$ @dmckee: As above, I precised question, thx. \$\endgroup\$ – Dariusz Woźniak Feb 28 '13 at 9:51
  • 1
    \$\begingroup\$ @aditsu: There are no restrictions oninput/output format \$\endgroup\$ – Dariusz Woźniak Feb 28 '13 at 20:26

10 Answers 10

4
\$\begingroup\$

K, 43 38

{d:(x;0N,x;y)@\:&~~':y;d[1]:(1_d 1),*|x;+d}

{a::*|x;+@[(x;{x,a};y)@\:&~~':y;1;1_]}

.

k){a::*|x;+@[(x;{x,a};y)@\:&~~':y;1;1_]}[1 2 3 4 5 6 7;9 9 15 15 15 30 9]
1 2 9 
3 5 15
6 6 30
7 7 9

Explanation

The function takes two vectors, x and y, as input

a::*|x takes the last value of x and assigns it to a.

&~~':y returns a list of indices where the y vector changes:

k){&~~':y}[1 2 3 4 5 6 7;9 9 15 15 15 30 9]
0 2 5 6

(x;{x,a};y) creates a three element list containing

1) the x vector,

2) the function {x,a} which takes an input variable x(not to be confused with the x vector which was input into the original function) and appends a, and

3) the y vector

(x;{x,a};y)@\:&~~':y takes this three element list and applies the indices to each element so that we return the x vector at those indices, the indices with a appended, and the y vector at those indices

k){b::*|x;(x;{x,b};y)@\:&~~':y}[1 2 3 4 5 6 7;9 9 15 15 15 30 9]
1 3 6 7
0 2 5 6 7
9 15 30 9

@[(x;{x,a};y)@\:&~~':y;1;1_] takes the matrix we've created and drops the first element of the second row.

k){b::*|x;@[(x;{x,b};y)@\:&~~':y;1;1_]}[1 2 3 4 5 6 7;9 9 15 15 15 30 9]
1 3  6  7
2 5  6  7
9 15 30 9

+ then transposes the result

k){b::*|x;+@[(x;{x,b};y)@\:&~~':y;1;1_]}[1 2 3 4 5 6 7;9 9 15 15 15 30 9]
1 2 9 
3 5 15
6 6 30
7 7 9
\$\endgroup\$
  • \$\begingroup\$ Would you kindly explain how it works? \$\endgroup\$ – DavidC Feb 28 '13 at 3:54
4
\$\begingroup\$

Mathematica, 40 47 / 40

Updated to handle the new rules:

#~Riffle~{##}[[-1,1]]&@@@Split[#,#2-#=={1,0}&]&

Example of use:

#~Riffle~{##}[[-1,1]]&@@@Split[#,#2-#=={1,0}&]& @
  {{1, 9}, {2, 9}, {3, 9}, {4, 15}, {5, 15}, {6, 30}, {7, 9}, {12, 9}}
{{1, 3, 9}, {4, 5, 15}, {6, 6, 30}, {7, 7, 9}, {12, 12, 9}}

Or, since "There are no restrictions on input/output format":

#[[1,1]]|Last@#&/@Split[#,#2-#=={1,0}&]&

Which when used as above outputs:

{1 | {3, 9}, 4 | {5, 15}, 6 | {6, 30}, 7 | {7, 9}, 12 | {12, 9}}
\$\endgroup\$
  • 2
    \$\begingroup\$ Perhaps you can explain the magic of the left hand side, #~Riffle~{##}[[-1, 1]] & @@@. (I already explained what SplitBy does.) \$\endgroup\$ – DavidC Feb 28 '13 at 14:10
  • \$\begingroup\$ Does it consider the gaps as required? Try d = {{1, 9}, {3, 9}, {4, 15}, {5, 15}, {6, 30}, {7, 9}} \$\endgroup\$ – Dr. belisarius Feb 28 '13 at 21:55
  • \$\begingroup\$ @belisarius there, now it does. \$\endgroup\$ – Mr.Wizard Mar 1 '13 at 7:48
  • \$\begingroup\$ Ok, +1 then. Nice hack \$\endgroup\$ – Dr. belisarius Mar 1 '13 at 14:04
3
\$\begingroup\$

J, 53 48 43

Explicit version (43 chars):

f=.4 :'|:(#&y,{.@:#&x)(1&,,:,&1)2(-.@=/\)x'

Tacit version (48 chars):

(|:@((#{.)~,{.@]#{:@[)[:(1&,,:,&1)2&(-.@=/\)@{:)

Ungolfed version:

(|:@(({.@[#~]),{:@[#~{.@])((1&,),:,&1)@(-.@(0&=)@(2&(-/\)@{:)))

Maybe not the best way, but had a lot of fun golfing it down.

   1 2 3 4 5 6 7 f 9 9 15 15 15 30 9
   or
   (...) 1 2 3 4 5 6 7,:9 9 15 15 15 30 9
1 2  9
3 5 15
6 6 30
7 7  9
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 106

function(c){for(var n={f:1,t:1,v:c[0]},r=[n],i=1,t;t=c[i++];)t^n.v?r.push(n={f:i,t:i,v:t}):n.t++;return r}

If input as a global called c is okay, then 101:

eval.bind(0,'for(var n={f:1,t:1,v:c[0]},r=[n],i=1,t;t=c[i++];)t^n.v?r.push(n={f:i,t:i,v:t}):n.t++;r')
\$\endgroup\$
1
\$\begingroup\$

Python, 86

n=input()
for s,v in n:
 i=1
 while[s+i,v]in n:n.remove([s+i,v]);i+=1
 print s,s+i-1,v
\$\endgroup\$
  • \$\begingroup\$ SyntaxError: unexpected EOF while parsing \$\endgroup\$ – aditsu Feb 28 '13 at 12:14
  • \$\begingroup\$ Nevermind, I figured out how to run it (same as my answer) \$\endgroup\$ – aditsu Feb 28 '13 at 19:54
1
\$\begingroup\$

APL 39

(p/n),((1⌽p)/n←⍎⍞),[1.1](p←1,2≠/v)/v←⍎⍞

The above takes screen input as the function runs via ←⍎⍞. If the vectors n and v are allowed to be predefined globals then the count reduces to 33:

(p/n),((1⌽p)/n),[1.1](p←1,2≠/v)/v

For n←1 2 3 4 5 6 7 and v←9 9 15 15 15 30 9

1 2  9
3 5 15
6 6 30
7 7  9
\$\endgroup\$
1
\$\begingroup\$

Python 2 - 82

r=p=[]
for x,y in input():
 if[x-1,y]==p[1:]:p[1]=x
 else:p=[x,x,y];r+=[p]
print r

Example usage:

echo "[[1,9],[2,9],[3,15],[4,15],[5,15],[6,30],[7,9]]"|python2 values.py
[[1, 2, 9], [3, 5, 15], [6, 6, 30], [7, 7, 9]]
echo "[[1,99],[2,99],[4,99],[6,99]]"|python2 values.py
[[1, 2, 99], [4, 4, 99], [6, 6, 99]]
\$\endgroup\$
1
\$\begingroup\$

GolfScript, 49/44 characters

~]2/{\.@.@~@~@=@@-)!&!{n\}*}*][n]%{.0=0=\-1=~]p}%

The solution takes input from STDIN and prints to STDOUT. If you transform the code into an expression working on lists it reduces to 44 characters.

{\.@.@~@~@=@@-)!&!{n\}*}*][n]%{.0=0=\-1=~]}%

Example:

1 9
2 9
3 15
4 15
5 15
6 30
7 9

[1 2 9]
[3 5 15]
[6 6 30]
[7 7 9]

This version does also work with gaps as required.

1 9
3 9
4 15
6 15
7 15

[1 1 9]
[3 3 9]
[4 4 15]
[6 7 15]
\$\endgroup\$
1
\$\begingroup\$

Golfscript - 42

~(~@@.@{.~5$=\3$)=&{;)}{:*;@]p*~\.}if}/@]p

Examples:

echo "[[1 9][2 9][3 15][4 15][5 15][6 30][7 9]]"|ruby golfscript.rb values.gs
[1 2 9]
[3 5 15]
[6 6 30]
[7 7 9]

echo "[[1 99][2 99][4 99][6 99]]"|ruby golfscript.rb values.gs
[1 2 99]
[4 4 99]
[6 6 99]

Explanation:

It takes the first pair [a b], and puts b, a, a1=a and the rest of the array on the stack. Then for each pair [x y] it compares y with b and x with a1+1. If equal then discards [x y] and increments a1, else pops and remembers [x y], prints [a a1 b] and pushes y, x, x1=x. At the end it prints the last [x x1 y].

\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6), 29 bytes (Non-competing)

a=>a.filter((x,y)=>a[y+1]!=x)

Try it

Enter a comma separated list of numbers.

f=
a=>a.filter((x,y)=>a[y+1]!=x)
oninput=_=>o.innerText=f(i.value.split`,`)
o.innerText=f((i.value="9,9,15,15,15,30,9").split`,`)
<input id=i><pre id=o>

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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