# [ 1, 2, 1, 1, 2, 1, 1 ] how to find adjacent elements in an array

Hello everyone,
I’d like to know if anyone can figure out how to find adjacent elements within an array.

ex: [2, 3, 5, 5]
at index 2 I have an equality of 2 times the value 5

ex: [1, 2, 1, 1, 1, 2, 1, 1],
at index 2 I have an equality of 3 times the value 1 and at index 7 I have an equality of 2 times the value 1

I need to find the index, the how many times (the adjacent values are equal) and what is the adjacent value.

Thank you so much in advance, I hope someone knows the way

What is your desired output structure? Something like this?

``````// input array
[2, 3, 5, 5]

// desired output array
[ [ 2, 2, 5] ] // array containing arrays structured as [index of first repeat occurrence, number of adjacent repeats, value that is repeated]

// input
[1, 2, 1, 1, 1, 2, 1, 1]

// desired output
[ [2, 3, 1], [6, 2, 1] ] // your example has a mistake I think? the second repeated chunk starts at index 6, not 7

``````

Or do you want a `Dictionary` associating the repeated chunks with their named properties (index of first repeat occurrence, # of repeats, etc.)?

something like this:

``````~findIt = {|array, index|
var count=0;
while({((index+count)<array.size)and:(array[index+count+1]==array[index])},{count=count+1});
[array[index], count+1, array[index+1]]
};

a = [1, 2, 1, 1, 1, 2, 1, 1];
b = ~findIt.value(a, 2);

a = [2, 3, 5, 5];
b = ~findIt.value(a, 2);
``````

this only looks to the right.

sam

If I understood OP correctly, this returns the wrong value for both inputs, see my post above.
`~findIt` always only returns one array even though it should return multiple arrays if there are multiple chunks of repeated values (one for every chunk). Also, it returns

``````[value at index of first repeated chunk, number of repeats, value at index+1 of first repeated chunk]
``````

``````[index of first repeat occurrence, number of adjacent repeats, value that is repeated]
``````

which is how I interpreted OP’s requirement. I also don’t get why `~findIt` needs an `index` argument?
Edit: I should probably wait for OP to respond instead of being pedantic about your solution This is perhaps a bit verbose and clunky, but it meets the above specification:

``````(
~repeats = {|array|
// return [index, number of repeats, the value] for each repeated value
var results = [];
var thisIndex = 0;
var thisCount = 1;
var thisValue = array;
array[1..].do{|val, i|
if(val == thisValue,
{thisCount = thisCount + 1},
{
if(
thisCount > 1,
{results = results.add([thisIndex, thisCount, thisValue])},
);
thisValue = val;
thisIndex = i + 1;
thisCount = 1;
}
)
};
if(thisCount > 1, {results = results.add([thisIndex, thisCount, thisValue])});
results;
}
)

a = [1, 2, 1, 1, 1, 2, 1, 1];
b = ~repeats.value(a);

a = [2, 3, 5, 5];
b = ~repeats.value(a);
``````
``````(
~findIt = { |array, repeatThreshold=2|
var chunks = [];
var chunkIndex = 0;
var chunkCount = 1;

if (array.every(_.isNumber).not) {
Error("Input contains non-numeric elements").throw;
};

while {chunkIndex < array.size} {
while {array[chunkIndex] == array[chunkIndex+chunkCount]} {
chunkCount = chunkCount + 1;
};

if (chunkCount >= repeatThreshold) {
chunks = chunks.add([chunkIndex, chunkCount, array[chunkIndex]])
};

chunkIndex = chunkIndex + chunkCount;
chunkCount = 1;
};
chunks
};
)

// testing:
(
var testArrays = [
[],
,
[2, 3, 5, 5],
[1, 2, 1, 1, 1, 2, 1, 1],
[2, 3, 5, 5, 7, 7, 8, 0, 7, 7, 7, 2, 1, 1, 1, 3],
{ 6.rand } ! 30
];

testArrays.do { |testArr|
"input: %, output: %\n".postf(testArr, ~findIt.(testArr))
};
)
``````

Edit: added error handling for non-numeric inputs.

1 Like

Hello,

The following code could be shortened and be constructed in more elegant ways, but it seems to work:

``````(
~findRepetitionsAt = { |collection|
var
collSize = collection.size-1,
at = 0;

collSize.do{
(collection.[at] == collection.[at+1]).if {
(collection.[at] == collection.[at+2]).if {
(
"index" + at ++ "," + (at+1) + "and" + (at+2) +
"have" + collection.[at] ++ ".\n <-" + collection.[at]++
":  3 times."
).postln;
at = at + 2
}{
(
"index" + at + "and" + (at+1) +
"have" + collection.[at] ++ ".\n <-" + collection.[at]++
":  twice."
).postln;
at = at + 1
}
}{
at = at + 1;
}
}
}
)

x = [2, 3, 5, 5];
y = [1, 2, 1, 1, 1, 2, 1, 1];

~findRepetitionsAt.(x)
~findRepetitionsAt.(y)
``````

‘while’ seems to be better than ‘.do’.

``````(
~findRepetitionsAt = { |array|
var
collSize = array.size-1,
at = 0;

while {
at <= collSize
}{
(array.[at] == array.[at+1]).if {
(array.[at] == array.[at+2] ).if {
(
"index" + at ++ "," + (at+1) + "and" + (at+2) +
"have" + array.[at] ++ ".\n <-" + array.[at]++
":  3 times."
).postln;
at = at + 2;
}{
(
"index" + at + "and" + (at+1) +
"have" + array.[at] ++ ".\n <-" + array.[at]++
":  twice."
).postln;
at = at + 1
}
}{
at = at + 1;
}
}
}
)

x = [2, 3, 5, 5];
y = [1, 2, 1, 1, 1, 2, 1, 1];
z = [1, 2, 1, 1, 1, 2, 1, 1, 1];

~findRepetitionsAt.(x)
~findRepetitionsAt.(y)
~findRepetitionsAt.(z)
``````

Your solution doesn’t work for more than 3 repeating values.

``````x = [1, 1, 1, 1];
~findRepetitionsAt.(x);

/*
index 0, 1 and 2 have 1.
<- 1:  3 times.
index 2 and 3 have 1.
<- 1:  twice.
*/
``````
1 Like

I would have proposed this:

``````
x = [1, 2, 1, 1, 1, 2, 1, 1];

// if you just want the groups
y = x.separate { |a, b| a.first != b.first };
// if you want the sizes
z = x.separate { |a, b| a.first != b.first }.collect { |a| a.size };
// if you want the indices
i =  ++ z.integrate.drop(-1);
// sizes and indices as subarrays
[z, i].flop
// select all larger than 1
[z, i].flop.select { |a| a.first > 1 }
// indices and sizes as dictionary
[i, z].flop.flat.asDict
// select all larger than 1
[i, z].flop.flat.asDict.select { |a| a > 1 }
// values at the indices left as an exercise
``````
5 Likes

Nice, I knew there was a neat functional solution out there somewhere! To match my spec above:

``````~findIt2 = { |arr|
var sizes = arr.separate { |a, b| a.first != b.first }.collect { |a| a.size };
var indices =  ++ sizes.integrate.drop(-1);
var values = arr[indices];
[indices, sizes, values].flop.select { |a| a > 1 }
};
``````
2 Likes

Thank you very much,
to all of you for the generosity of answers!
I think the latter is the answer that best suits what I had asked.