-2

I have following array:


array(174) {
  [0]=>
  string(5) "3.0.3"
  [1]=>
  string(5) "3.0.2"
  [2]=>
  string(5) "3.0.1"
  [3]=>
  string(5) "3.0.0"
  [9]=>
  string(5) "2.9.5"
  [10]=>
  string(5) "2.9.4"
  [11]=>
  string(5) "2.9.3"
  [12]=>
  string(5) "2.9.2"
  [13]=>
  string(5) "2.9.1"
  [14]=>
  string(5) "2.9.0"
  [18]=>
  string(6) "2.8.11"
  [19]=>
  string(6) "2.8.10"
  [20]=>
  string(5) "2.8.9"
}

I need to find the highest 3rd number for unique pair of first two numbers x.x. With this example the expected result must be:

3.0.3, 2.9.5, 2.8.11

This is what I tried:

foreach ($array as $key => $value) {
    $test = substr($value, 0, 3);
    $a = strtr($value, array('.' => '', ',' => ''));
    $b = (int) $a;
    $c = substr($b, 0, 2);
    $new_array = array($c);
    $result = array_unique($new_array);
    print_object($result);
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
user2450639
  • 196
  • 1
  • 14
  • 3
    although I really like brain teasers. We expect you to have tried something already, did you try anything? – izk Mar 30 '16 at 08:37
  • This function may help: http://php.net/manual/en/function.explode.php – Reversal Mar 30 '16 at 08:43
  • Yes I am trying to figure it out. I was trying to get the first two values first, and then compare it, but I can't do that. foreach ($array as $key => $value) { $test = substr($value, 0, 3); $a = strtr($value, array('.' => '', ',' => '')); $b = (int)$a; $c = substr($b, 0, 2); $new_array = array($c); $result = array_unique($new_array) ; print_object($result); } – user2450639 Mar 30 '16 at 08:45
  • use [usort()](http://www.php.net/manual/en/function.usort.php) with PHP's [version_compare()](http://www.php.net/manual/en/function.version-compare.php) as a starting point; then perhaps an [array_walk()](http://www.php.net/manual/en/function.array-walk.php) to break into major/minor groups – Mark Baker Mar 30 '16 at 08:47

3 Answers3

2

First you must group the versions by the major version. After that you just need to sort the resulted groups using the version_compare function in a descending order and to return the first element of each group:

$versions = array("3.0.3", "3.0.2", "3.0.1", "3.0.0", "2.9.5", "2.9.4",
    "2.9.3", "2.9.2", "2.9.1", "2.9.0", "2.8.11", "2.8.10", "2.8.9"
);

$groupedVersions = array();
foreach ($versions as $version) {
    preg_match('/^\d+\.\d+/', $version, $majorVersion);
    if (!isset($groupedVersions[$majorVersion[0]])) {
        $groupedVersions[$majorVersion[0]] = array($version);
    } else {
        $groupedVersions[$majorVersion[0]][] = $version;
    }
}

$groupedVersions = array_map(function ($versions) {
    usort($versions, 'version_compare');
    return array_reverse($versions);
}, $groupedVersions);

$latestVersions = array_reduce($groupedVersions, function ($carry, $versions) {
    $carry[] = $versions[0];
    return $carry;
}, array());

echo '<pre>';
var_dump($latestVersions);
echo '</pre>';

The result would be:

array(3) {
  [0]=>
  string(5) "3.0.3"
  [1]=>
  string(5) "2.9.5"
  [2]=>
  string(6) "2.8.11"
}
Mihai Matei
  • 24,166
  • 5
  • 32
  • 50
2

When grouping array data in PHP, it is often most efficient to generate temporary keys based on the grouping criteria.

I'll use some version terminology based on the accepted answer from: What every digit means in software version (1.7.1.0, for example)?

In this case, the first two integers (Major & Minor release) are used (and they must remain separated by a delimiting character to avoid data collisions). As you iterate, check if the temporary key exists in the output collection.  If not, add it.  If so, check if the third integer (Maintenance release) is greater than the stored Maintenance release for that Major-Minor release group -- if so, replace the stored Major-Minor release group's value.

This is very concisely performed in my snippet below.  If you need the output to be indexed, call array_values(), otherwise omit that step.

Code: (Demo)

$versions = [
    "3.0.1", "3.0.3", "3.0.2", "3.0.0",
    "2.9.4", "2.9.3", "2.9.2", "2.9.5", "2.9.1", "2.9.0",
    "2.8.1", "2.8.11", "2.8.10"
];
    
foreach ($versions as $version) {
    $ints = explode('.', $version, 3);
    $tempKey = "{$ints[0]}.{$ints[1]}";
    if (!isset($result[$tempKey]) || version_compare($version, $result[$tempKey], 'gt')) { 
        $result[$tempKey] = $version;
    }
}
    
var_export(array_values($result));

Output:

array (
  0 => '3.0.3',
  1 => '2.9.5',
  2 => '2.8.11',
)

Using version_compare() is an appropriate tool to compare versions, but it can be avoided if you wish to make a simple integer to integer comparison on the Maintenance release value.  This will cost a little more memory because you will also need to store an array of grouped Maintenance release integers to make this comparison.  I am recommending just comparing the full version strings instead.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
1

You can use only explode() and compare last value for each major varsion, then to build associative array with major and minor version:

    <?php
        $arr = array(0 => "3.0.3", 1 => "3.0.2", 2 => "3.0.1", 3 => "3.0.0", 9 => "2.9.5", 10 => "2.9.4", 11 => "2.9.3", 12 => "2.9.2", 13 => "2.9.1", 14 => "2.9.0", 18 => "2.8.11", 19 => "2.8.10", 20 => "2.8.9");
        $versions = array();
        $final = array();
        foreach ($arr as $version) {
            $explode = explode('.', $version); // split all parts
            $end = '';
            $begin = '';
            if (count($explode) > 0) {
                $end = array_pop($explode); // removes the last element, and returns it

                if (count($explode) > 0) {
                    $begin = implode('.', $explode); // glue the remaining pieces back together
                }
            }
            if(!empty($versions[$begin])){
                if($versions[$begin] < $end){
                    $versions[$begin] = $end;
                }
            }else{
                $versions[$begin] = $end;
            }
        }
        foreach($versions as $key=>$value){
            $final[] = "$key.$value";
        }
        print_r($final);
    ?>

Output:

Array
(
    [0] => 3.0.3
    [1] => 2.9.5
    [2] => 2.8.11
)
mitkosoft
  • 5,262
  • 1
  • 13
  • 31