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\$
8
  • 1
    \$\begingroup\$ Should it be a full program with input/output or just a function? \$\endgroup\$
    – grc
    Feb 28, 2013 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\$ Feb 28, 2013 at 1:50
  • \$\begingroup\$ @grc: I precised rules, thank you. \$\endgroup\$ Feb 28, 2013 at 9:50
  • \$\begingroup\$ @dmckee: As above, I precised question, thx. \$\endgroup\$ Feb 28, 2013 at 9:51
  • 1
    \$\begingroup\$ @aditsu: There are no restrictions oninput/output format \$\endgroup\$ Feb 28, 2013 at 20:26

9 Answers 9

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\$
1
  • \$\begingroup\$ Would you kindly explain how it works? \$\endgroup\$
    – DavidC
    Feb 28, 2013 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\$
4
  • 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, 2013 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\$ Feb 28, 2013 at 21:55
  • \$\begingroup\$ @belisarius there, now it does. \$\endgroup\$
    – Mr.Wizard
    Mar 1, 2013 at 7:48
  • \$\begingroup\$ Ok, +1 then. Nice hack \$\endgroup\$ Mar 1, 2013 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\$
2
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\$

Your Answer

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

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