Group adjacent values

7

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.

Dariusz Woźniak

Posted 2013-02-27T22:33:26.863

Reputation: 1 131

1Should it be a full program with input/output or just a function? – grc – 2013-02-28T01:39:23.400

1Is 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? – dmckee --- ex-moderator kitten – 2013-02-28T01:50:08.067

@grc: I precised rules, thank you. – Dariusz Woźniak – 2013-02-28T09:50:58.360

@dmckee: As above, I precised question, thx. – Dariusz Woźniak – 2013-02-28T09:51:58.467

Regarding possible gaps in the sequence numbers: should the output also contain gaps? What would the desired output for "1 99\n2 99\n4 99\n6 99" be? – Howard – 2013-02-28T10:36:28.430

@Howard: Yes, output should also contain gaps as there must be continuity on Sequence Number; so for your input it will be: 1 2 99\n4 4 99\n6 6 99. – Dariusz Woźniak – 2013-02-28T11:54:58.583

What is the format of the input? If we're talking about a function, then can it take a data structure directly (in languages where that is applicable)? Can it also return a data structure? – aditsu quit because SE is EVIL – 2013-02-28T12:23:33.237

1@aditsu: There are no restrictions oninput/output format – Dariusz Woźniak – 2013-02-28T20:26:27.627

Answers

4

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

tmartin

Posted 2013-02-27T22:33:26.863

Reputation: 3 917

Would you kindly explain how it works? – DavidC – 2013-02-28T03:54:39.630

4

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}}

Mr.Wizard

Posted 2013-02-27T22:33:26.863

Reputation: 2 481

2Perhaps you can explain the magic of the left hand side, #~Riffle~{##}[[-1, 1]] & @@@. (I already explained what SplitBy does.) – DavidC – 2013-02-28T14:10:19.070

Does it consider the gaps as required? Try d = {{1, 9}, {3, 9}, {4, 15}, {5, 15}, {6, 30}, {7, 9}} – Dr. belisarius – 2013-02-28T21:55:51.850

@belisarius there, now it does. – Mr.Wizard – 2013-03-01T07:48:00.840

Ok, +1 then. Nice hack – Dr. belisarius – 2013-03-01T14:04:07.463

3

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

randomra

Posted 2013-02-27T22:33:26.863

Reputation: 19 909

1

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')

Ry-

Posted 2013-02-27T22:33:26.863

Reputation: 5 283

1

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

grc

Posted 2013-02-27T22:33:26.863

Reputation: 18 565

SyntaxError: unexpected EOF while parsing – aditsu quit because SE is EVIL – 2013-02-28T12:14:01.773

Nevermind, I figured out how to run it (same as my answer) – aditsu quit because SE is EVIL – 2013-02-28T19:54:48.093

1

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

Graham

Posted 2013-02-27T22:33:26.863

Reputation: 3 184

1

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]]

aditsu quit because SE is EVIL

Posted 2013-02-27T22:33:26.863

Reputation: 22 326

1

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].

aditsu quit because SE is EVIL

Posted 2013-02-27T22:33:26.863

Reputation: 22 326

1

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]

Howard

Posted 2013-02-27T22:33:26.863

Reputation: 23 109

0

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>

Shaggy

Posted 2013-02-27T22:33:26.863

Reputation: 24 623