I'm suffering for an hour, I can not select ranges from the array. Suppose there is an array [0,1,2,3,5,6,7,9,13,20] . At the output I want to get the line 0-3,5-7,9,13,20 .
I tried to cycle through the array and use next and prev to replace the numbers with a dash or add a comma, but some garbage comes out. Prompt, for sure there are simple algorithms for this task or helper in Yii2?

  • Is the sequence of only two consecutive numbers differing by 1 interval? That is, what is the answer expected from the array [0,1,3] -> 0-1,3 or 0,1,3 ? - Visman pm

3 answers 3

But the primitive cycle, just php :)

 function array_to_string($arr) { sort($arr, SORT_NUMERIC); $kk = count($arr); $result = ''; for ($i = 0; $i <= $kk; $i++) { if (isset($arr[$i])) { if (!count($a)) { $a[] = $arr[$i]; continue; } if ($arr[$i] == end($a) + 1) { $a[] = $arr[$i]; continue; } } if (count($a) < 3) { $result .= implode(',', $a) . ','; } else { $result .= reset($a) . '-'. end($a) . ','; } if (isset($arr[$i])) { $a = [$arr[$i]]; } } return trim($result, ','); } echo array_to_string([0,1,2,3,5,6,7,9,13,20]) . "<br>\n"; echo array_to_string([0,1,3,5,6,7,8,9,10,11,12,13,20,21]) . "<br>\n"; 

Result

 0-3,5-7,9,13,20 0,1,3,5-13,20,21 
  • Thank you, this is exactly what you need. True, I presented the result as an array, not a string, and instead of trim by comma I made implode. It turned out this way to add spaces after commas. - ilyaplot

First option:

 $myArray = [0,1,2,3,5,6,7,9,13,20]; //last value is dropped so add something useless to be dropped array_push($myArray, null); $rangeArray = array(); array_walk($myArray, function($val) use (&$rangeArray){ static $oldVal, $rangeStart; if (is_null($rangeStart)) goto init; if ($oldVal+1 == $val) { $oldVal = $val; return; } if ($oldVal == $rangeStart) { array_push($rangeArray, $rangeStart); goto init; } array_push($rangeArray, $rangeStart . '-' . $oldVal); init: { $rangeStart = $val; $oldVal = $val; } }); echo '<pre>'.print_r($rangeArray, true).'</pre>'; 

The second option:

 $numbers = [0,1,2,3,5,6,7,9,13,20]; $ranges[] = array($numbers[0],$numbers[0]); // initial value foreach ($numbers as $number) { $range = array_pop($ranges); $extend = ($range[1] == $number-1); $ranges[] = array($range[0],$extend ? $number : $range[1]); if (!$extend) $ranges[] = array($number,$number); } echo '<pre>'.print_r($ranges,TRUE).'</pre>'; 

will contain an array of the following form:

 Array ( [0] => Array ( [0] => 0 [1] => 0 ) [1] => Array ( [0] => 0 [1] => 3 ) [2] => Array ( [0] => 5 [1] => 7 ) [3] => Array ( [0] => 9 [1] => 9 ) [4] => Array ( [0] => 13 [1] => 13 ) [5] => Array ( [0] => 20 [1] => 20 ) ) 

that is, it is clear from it that if the element in cell 0 and 1 is the same, it means that it is unique, if it is different, then this is the range. You can filter this later:

 foreach ($ranges as $range) { $output[] = ($range[0] == $range[1]) ? $range[0] : $range[0].'-'.$range[1]; } echo '<pre>'.print_r($output,TRUE).'</pre>'; 

will already output

 Array ( [0] => 0-3 [1] => 5-7 [2] => 9 [3] => 13 [4] => 20 ) 

Examples are taken from https://codereview.stackexchange.com/questions/80080/aggregate-array-values-into-ranges

There you can also see a couple of examples.

  • The second option, at your zero position, returns an array with two zeros, and at the first position, zero returns again. - Visman
  • AAAAAAAA, goto! - etki
  • @Etki are you afraid?) Volkovs are afraid not to go to the forest) - Alexey Shimansky
  • it is there they nakadyvayutsya - etki

Probably, there is a more optimal solution, but in haste it turned out quite suitable code. Comments wrote more than code ...

 //упорядоченный входной массив $arr = [0,2,3,5,6,7,9,13,15,16,17,20]; //сюда будем складывать ranges: ["0","2-3","5-7",...] $ranges = []; //а в этой переменной будем хранить текущий range: [from, to] $currentRange = [$arr[0],$arr[0]]; //перебираем со второго элемента и до +последнего for($i = 1; $i <= count($arr); $i++){ //на последней итерации, когда $i > count($arr), просто закроем последний range if(!isset($arr[$i])) { $ranges[] = $currentRange[0] !== $currentRange[1] ? implode('-', $currentRange) : $currentRange[0]; } else { //если последний элемент текущего range равен "текущее значение - 1" - продляем range на текущий элемент if ($currentRange[1] === intval($arr[$i]) - 1) { $currentRange[1] = $arr[$i]; } else { //закрываем текущий range (скидываем в ranges новую строку) $ranges[] = $currentRange[0] !== $currentRange[1] ? implode('-', $currentRange) : $currentRange[0]; //открываем новый range с текущего места $currentRange = [$arr[$i], $arr[$i]]; } } } //склеиваем результирующий массив в строку через запятую $resultStr = implode(',', $ranges);