How to count the consecutive duplicate values in an array?
Asked Answered
H

6

14

I have an array like this:

$arr = array(1, 1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 3);

I found the function array_count_values(), but it will group all of the same values and count the occurrences without respecting breaks in the consecutive sequences.

$result[1] = 5
$result[2] = 4
$result[3] = 3

How can I group each set of consecutive values and count the length of each sequence? Notice there are two sets of sequences for the numbers 1, 2, and 3.

The data that I expect to generate needs to resemble this:

[1] = 3;
[2] = 2;
[3] = 2;
[1] = 2;
[2] = 2;
[3] = 1;
Homophile answered 10/3, 2011 at 3:57 Comment(1)
This is called run-length encoding.Izy
V
16

It can be done simply manually:

$arr = array(1,1,1,2,2,3,3,1,1,2,2,3);

$result = array();
$prev_value = array('value' => null, 'amount' => null);

foreach ($arr as $val) {
    if ($prev_value['value'] != $val) {
        unset($prev_value);
        $prev_value = array('value' => $val, 'amount' => 0);
        $result[] =& $prev_value;
    }

    $prev_value['amount']++;
}

var_dump($result);
Valerievalerio answered 10/3, 2011 at 4:3 Comment(0)
D
4

What about PHP's array_count_values function?

<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>

output:

Array
(
    [1] => 2
    [hello] => 2
    [world] => 1
)
Dupery answered 28/5, 2016 at 15:22 Comment(2)
I have no idea why so many people have upvoted this incorrect answer. To spin my phrasing, this is the correct answer to a different question.Diastole
The problem with this answer is that it ignores the consecutive requirement from the question. This counts all values, regardless of position. When you run this function with the array from the question you do not get the desired resultTheatricals
D
4

My suggestion is to extract&remove the first value from the array prior to entering the loop and use a temporary array ($carry) to track whether each new value matches the key in the carry array. If so, increment it. If not, push the completed sequence count into the result array and overwrite the carry with the new value and set the counter to 1. When the loop finishes, push the lingering carry into the result set. My snippet does not check if the input array is empty; if necessary, add that condition to your project.

Code: (Demo)

$array = [1,1,1,2,2,3,3,1,1,2,2,3];
 
$result = [];
$carry = [array_shift($array) => 1];
 
foreach ($array as $value) {
    if (isset($carry[$value])) {
        ++$carry[$value];
    } else {
        $result[] = $carry;
        $carry = [$value => 1];
    }
}
$result[] = $carry;
print_r($result);

Output: (condensed to reduce page bloat)

[
    [1 => 3],
    [2 => 2],
    [3 => 2],
    [1 => 2],
    [2 => 2],
    [3 => 1],
]

If you'd rather implement a zerkms-style, modify-by-reference style technique, the following snippet provides the same result as the above snippet.

Effectively, it pushes every newly encountered value as an associative, single-element array into the indexed result array. Because the pushed subarray is declared as a variable ($carry) then assigned-by-reference (= &) to the result array, incrementation of $carry will be applied to the deeply nested value in the result array. The output array requires the additional depth in its structure so that a given value which occurs multiple times can be reliably stored.

Code: (Demo)

$result = [];
$carry = [];
foreach ($array as $value) {
    if ($carry && key($carry) === $value) {
        ++$carry[$value];
    } else {
        unset($carry);
        $carry = [$value => 1];
        $result[] = &$carry;
    }
}
unset($carry);
print_r($result);

Unsetting the reference variable $carry after the loop may not be necessary, but if there is any potential re-use of that variable within the variable's scope, it will be important to uncouple the reference with unset().

And just for fun, here is a hideous regex-infused approach that works with the sample data: Demo

Diastole answered 30/5, 2020 at 8:1 Comment(0)
L
-1
function findRepetitions($times, $array) {

    $values = array_unique($array);

    $counts = [];
    foreach($values as $value) {
        $counts[] = ['value' => $value, 'count' => 0];
    }

    foreach ($array as $value) {
        foreach ($counts as $key => $count) {
            if ($count['value'] === $value) {
                $counts[$key]['count']++;
            }
        }
    }

    $repetitions = [];
    foreach ($counts as $count) {
        if ($count['count'] === $times) {
            $repetitions[] = $count['value'];
        }
    }

    return $repetitions;
}
Leopoldine answered 24/1, 2015 at 12:6 Comment(1)
This code-only answer needs its explanation. Why is there a $times parameter?Diastole
B
-2
$current = null;
foreach($your_array as $v) {
    if($v == $current) {
        $result[count($result)-1]++;
    } else {
        $result[] = 1;
        $current = $v;
    }
}

var_dump($result);
Briard answered 10/3, 2011 at 4:4 Comment(2)
a one liner for your solution would be $result = explode( ',' , implode(',', array_count_values($your_array) ) );Salsala
This answer loses the relationship between the number repeated in a given sequence and the length of the sequence. Amber, please improve this code only answer.Diastole
I
-2

Here is the way that I would do it:

function SplitIntoGroups($array)
{
    $toReturnArray = array();
    $currentNumber = $array[0];
    $currentCount = 1;
    for($i=1; $i <= count($array); $i++)
    {
        if($array[$i] == $currentNumber)
        {
            $currentCount++;
        }
        else
        {
            $toReturnArray[] = array($currentNumber, $currentCount);
            $currentNumber = $array[$i];
            $currentCount = 1;
        }
    }

    return $toReturnArray;
}

$answer = SplitIntoGroups(array(1,1,1,2,2,3,3,1,1,2,2,3));
for($i=0; $i<count($answer); $i++)
{
    echo '[' . $answer[$i][0] . '] = ' . $answer[$i][1] . '<br />';
}
Irrefrangible answered 10/3, 2011 at 4:13 Comment(2)
never use count($array) inside a for loop, its calculated in every loop, foreach() is a far better loop structure in for an array in php anywayProgressist
This snippet generates Undefined Offset notices and should not be used.Diastole

© 2022 - 2024 — McMap. All rights reserved.