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;
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Jeg Bagus
  • 4,895
  • 9
  • 43
  • 54

6 Answers6

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);
zerkms
  • 249,484
  • 69
  • 436
  • 539
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
)
Merlevede
  • 8,140
  • 1
  • 24
  • 39
  • 6
    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. – mickmackusa May 30 '20 at 07:15
  • 4
    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 result](https://3v4l.org/IcfbH) – Machavity Sep 14 '22 at 12:54
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

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
-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;
}
Michele Carino
  • 1,043
  • 2
  • 11
  • 25
-2
$current = null;
foreach($your_array as $v) {
    if($v == $current) {
        $result[count($result)-1]++;
    } else {
        $result[] = 1;
        $current = $v;
    }
}

var_dump($result);
Amber
  • 507,862
  • 82
  • 626
  • 550
  • a one liner for your solution would be $result = explode( ',' , implode(',', array_count_values($your_array) ) ); – epynic Feb 26 '15 at 07:00
  • 2
    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. – mickmackusa May 30 '20 at 07:29
-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 />';
}
Flipper
  • 2,589
  • 3
  • 24
  • 32
  • 1
    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 anyway –  Mar 10 '11 at 04:43
  • This snippet generates Undefined Offset notices and should not be used. – mickmackusa May 30 '20 at 07:37