Add order column to array to indicate rank from oldest to youngest
Asked Answered
S

4

-1

In PHP7, If I have this array:

$array = [
    ["name" => "Jon", "age" => 44],
    ["name" => "Bob", "age" => 32],
    ["name" => "Jim", "age" => 103],
    ["name" => "Ted", "age" => 19]
];

What is the most elegant way to process this array to add an extra column indicating who is the oldest to who is the youngest like so...

[
    ["name" => "Jon", "age" => 44, "order" => 2],
    ["name" => "Bob", "age" => 32, "order" => 3],
    ["name" => "Jim", "age" => 103, "order" => 1],
    ["name" => "Ted", "age" => 19, "order" => 4]
]
Seymourseys answered 16/5, 2017 at 17:34 Comment(0)
M
0

Without a point of reference to determine which is the oldest user you're stuck either doing a multi loop comparison or a sort then insert.

A Multi loop comparasin would look something like

<?php
$array = [
  ["name" => "Jon", "age" => 44],
  ["name" => "Bob", "age" => 32],
  ["name" => "Jim", "age" => 103],
  ["name" => "Ted", "age" => 19]
];

$count = count($array);

for($index = 0; $index < $count; ++$index) {
  $order = 1;
  $age = $array[$index]["age"];
  for($index2 = 0; $index2 < $count; ++$index2) {
    if($array[$index2]["age"] > $age) {
      ++$order;
    }
  }

  $array[$index]["order"] = $order;
}

echo "<pre>";
var_dump($array);
echo "</pre>";

Sort then insert would involve array_walk and uasort

From the docs

<?php
$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");

function test_alter(&$item1, $key, $prefix)
{
  $item1 = "$prefix: $item1";
}

function test_print($item2, $key)
{
  echo "$key. $item2<br />\n";
}

echo "Before ...:\n";
array_walk($fruits, 'test_print');

array_walk($fruits, 'test_alter', 'fruit');
echo "... and after:\n";

array_walk($fruits, 'test_print');

and docs

<?php
// Comparison function
function cmp($a, $b) {
  if ($a == $b) {
    return 0;
  }
  return ($a < $b) ? -1 : 1;
}

// Array to be sorted
$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);
print_r($array);

// Sort and print the resulting array
uasort($array, 'cmp');
print_r($array);

so for your example it would be something like this:

<?php
function walkFunc(&$item, $key) {
  $item["order"] = $key + 1;
}

// as @waterloomatt mentioned the Spaceship operator is meant for this
function sortComp($a, $b) {
  return ($a["age"] <=> $b["age"]) * -1;
}

$array = [
  ["name" => "Jon", "age" => 44],
  ["name" => "Bob", "age" => 32],
  ["name" => "Jim", "age" => 103],
  ["name" => "Ted", "age" => 19]
];

uasort($array, 'sortComp');
array_walk($array, 'walkFunc');

echo "<pre>";
var_dump($array);
echo "</pre>";
Materialist answered 16/5, 2017 at 17:57 Comment(0)
D
1

Here we are using multiple function to obtain desired output. array_column for extracting column age, the we are reverse sorting the column using arsort($columns); then getting array_values, then we are flipping array to get its order for age using array_flip, at last we are using array_map and array_merge to iterate and add a column in the array.

Try this code snippet here

<?php
ini_set('display_errors', 1);
$array = [
    ["name" => "Jon", "age" => 44],
    ["name" => "Bob", "age" => 32],
    ["name" => "Jim", "age" => 103],
    ["name" => "Ted", "age" => 19]
];
$columns=  array_column($array, "age");//obtaining column age
arsort($columns);//sorting column in reverse

$column=array_flip(array_values($columns));//getting order for age
$result=array_map(function($value) use(&$column){
    $value=  array_merge($value,array("order"=>$column[$value["age"]]+1));//adding age column to array by adding index with 1
    return $value;

}, $array);
print_r($result);

Output:

Array
(
    [0] => Array
        (
            [name] => Jon
            [age] => 44
            [order] => 2
        )

    [1] => Array
        (
            [name] => Bob
            [age] => 32
            [order] => 3
        )

    [2] => Array
        (
            [name] => Jim
            [age] => 103
            [order] => 1
        )

    [3] => Array
        (
            [name] => Ted
            [age] => 19
            [order] => 4
        )

)
Dorser answered 16/5, 2017 at 17:42 Comment(1)
This gapped ranking snippet acts awkwardly when there are ties. See how there is no 3rd rank with this sample set: 3v4l.org/9r7dbTears
A
0

Not going to lie, I just wanted to use the new spaceship operator. This does not add a new column but will instead sort the array descending by age.

<?php

$array = [
    ["name" => "Jon", "age" => 44],
    ["name" => "Bob", "age" => 32],
    ["name" => "Jim", "age" => 103],
    ["name" => "Ted", "age" => 19]
];

usort($array, function ($a, $b) { 
    return ($a["age"] <=> $b["age"]) * -1; 
});

print_r($array);
Azarria answered 16/5, 2017 at 17:52 Comment(1)
This answer does not attempt to answer the question. ($a["age"] <=> $b["age"]) * -1; is more simply written as $b["age"] <=> $a["age"];Tears
M
0

Without a point of reference to determine which is the oldest user you're stuck either doing a multi loop comparison or a sort then insert.

A Multi loop comparasin would look something like

<?php
$array = [
  ["name" => "Jon", "age" => 44],
  ["name" => "Bob", "age" => 32],
  ["name" => "Jim", "age" => 103],
  ["name" => "Ted", "age" => 19]
];

$count = count($array);

for($index = 0; $index < $count; ++$index) {
  $order = 1;
  $age = $array[$index]["age"];
  for($index2 = 0; $index2 < $count; ++$index2) {
    if($array[$index2]["age"] > $age) {
      ++$order;
    }
  }

  $array[$index]["order"] = $order;
}

echo "<pre>";
var_dump($array);
echo "</pre>";

Sort then insert would involve array_walk and uasort

From the docs

<?php
$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");

function test_alter(&$item1, $key, $prefix)
{
  $item1 = "$prefix: $item1";
}

function test_print($item2, $key)
{
  echo "$key. $item2<br />\n";
}

echo "Before ...:\n";
array_walk($fruits, 'test_print');

array_walk($fruits, 'test_alter', 'fruit');
echo "... and after:\n";

array_walk($fruits, 'test_print');

and docs

<?php
// Comparison function
function cmp($a, $b) {
  if ($a == $b) {
    return 0;
  }
  return ($a < $b) ? -1 : 1;
}

// Array to be sorted
$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);
print_r($array);

// Sort and print the resulting array
uasort($array, 'cmp');
print_r($array);

so for your example it would be something like this:

<?php
function walkFunc(&$item, $key) {
  $item["order"] = $key + 1;
}

// as @waterloomatt mentioned the Spaceship operator is meant for this
function sortComp($a, $b) {
  return ($a["age"] <=> $b["age"]) * -1;
}

$array = [
  ["name" => "Jon", "age" => 44],
  ["name" => "Bob", "age" => 32],
  ["name" => "Jim", "age" => 103],
  ["name" => "Ted", "age" => 19]
];

uasort($array, 'sortComp');
array_walk($array, 'walkFunc');

echo "<pre>";
var_dump($array);
echo "</pre>";
Materialist answered 16/5, 2017 at 17:57 Comment(0)
T
0

To assign dense ranks to each row based on the age values, avoid using nested loops and convoluted piles of array functions. These techniques will have poor time complexity (efficiency) and/or will be very hard to maintain.

Instead, enjoy the sweet convenience of reference variables. Loop the input array just once and assign references to the new, desired order element in each row. To see what this does, view this -- these null values will be replaced by the calculated order.

Next, sort the keys of the ref array in a descending order.

Finally, iterate the sorted array and assign incremented values by reference. Because the ref array elements are "linked" to the $array rows, the placeholder elements instantly receive the new values.

The first loop is "linear" -- meaning that that it doesn't need to iterate the array more than once. The second loop may do fewer iterations than the first if duplicate ages are encountered.

Code: (Demo)

foreach ($array as &$row) {
    $row['order'] = &$ref[$row['age']];
}
krsort($ref);
$order = 0;
foreach ($ref as &$v) {
    $v = ++$order;
}
var_export($array);
Tears answered 22/9, 2022 at 13:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.