I have for example three arrays in one array:
$foo = [
"id" => [1, 3, 8],
"name" => ['one', 'three', 'eight'],
"isLarge" => [false, true, true]
];
I want simple combine these arrays as exactly the reverse operation to array_column, basically I want to obtain:
$bar = [[
"id" => 1,
"name" => "one",
"isLarge" => false
], [
"id" => 3,
"name" => "three",
"isLarge" => true
], [
"id" => 8,
"name" => "eight",
"isLarge" => true
]];
Thanks in advance!
One solution would be:
$bar = [];
for ($i = 0; $i < count($foo['id']); $i++)
$bar[] = [
"id" => $foo["id"][$i],
"name" => $foo["name"][$i],
"isLarge" => $foo["isLarge"][$i]
];
But this seems a bit cumbersome.
You can avoid hardcoding the column names by looping over the first row of your array, and then using a combination of array_combine and array_column to transpose it:
$keys = array_keys($foo);
$bar = [];
foreach(array_keys($foo[$keys[0]]) as $columnNumber) {
$bar[] = array_combine($keys, array_column($foo, $columnNumber));
}
This takes each vertical "slice" of your 2d array, and uses each one to create a row in the output.
See https://3v4l.org/nscrh for a demo
On the off-chance that your resulting array doesn't need the column names and all you need is a pure transposition, you can use a much quicker option:
$bar = array_map(null, ...array_values($foo));
Related
I have two arrays one contains ids that's going to check if it exists on the second array which is an associative array:
Array 1: [1,2,11, 4]
Array 2:
[["id" => 1, "name" => "abc"], ["id" => 2, "name"=> "xyz"]]
Currently using a nested foreach to iterate over and match them but I wanted to know if there was anything more efficient as the arrays will be a lot larger.
$item = [1,2,11, 4];
$data = [["id" => 1, "name" => "abc"], ["id" => 2, "name"=> "xyz"]];
foreach($items as $item)
{
foreach($data as $key => $products)
{
foreach($products as $product)
{
if($product['id'] == $item)
{
echo $product['name'];
}
}
}
}
Had a look at this and this but none really fit my scenario.
Any help appreciated.
array_filter would be an option:
$ids = [ 1, 2, 11, 4 ];
$data = [ [ 'id' => 1, 'name' => 'abc' ], [ 'id' => 2, 'name' => 'xyz' ], [ 'id' => 3, 'name' => 'nono' ] ];
$result = array_filter($data, fn($value) => (in_array($value['id'], $ids)));
You can use array_column() to get all product ids as an indexed array.
Then you can use array_intersect() to fetch the id's that exists in both arrays.
$item = [1,2,11, 4];
$products = [["id" => 1, "name" => "abc"], ["id" => 2, "name"=> "xyz"]];
// Get the ids of all products as an indexed array
$prodIds = array_column($products, 'id');
// Check the id's that exists in both
$existingIds = array_intersect($item, $prodIds);
Demo: https://3v4l.org/HJSoq
Of if you rather do it as a one-liner:
$existingIds = array_intersect($item, array_column($products, 'id'));
You can also use the array_map and the anonymous function.
$item = [1, 2, 11, 4];
$products = [["id" => 1, "name" => "abc"], ["id" => 2, "name" => "xyz"]];
$result = array_map(function ($row) use ($item) {
return (in_array($row['id'], $item))?$row:false;
}, $products);
Result dump:
Array
(
[0] => Array
(
[id] => 1
[name] => abc
)
[1] => Array
(
[id] => 2
[name] => xyz
)
)
Please, consider the following arrays:
$reference = array(
'080604' => 4,
'080703' => 4,
'080734' => 2,
'080819' => 2,
'088341' => 2,
'805238' => 20,
'805283' => 4,
'805290' => 2,
'805849' => 2,
'806051' => 2,
'806068' => 2,
);
$test = array(
'080604' => 2,
'080703' => 4,
'080819' => 1,
'088341' => 2,
'805238' => 20,
'805283' => 4,
'805290' => 2,
'805849' => 2,
'806051' => 2,
'806068' => 2,
);
They are quite similar, but can have some various differences, e.g. it's possible that:
- some keys of $reference are not present in $test at all
- some keys of $test are not present in $reference at all
- all keys are present, but the values in $reference and $test are different (sometimes $reference value is bigger than $test and sometimes the value of $test is bigger than $reference)
I need to find out the differences automatically and to output them in a way, that not only the difference in count itself, but also a description is provided, e.g.
$result = [
'080604' => [
'reference' => 4,
'test' => 2
]
];
If some value is in only one of the lists:
$result = [
'1234567890' => [
'reference' => 0,
'test' => 2
]
];
or something like that.
Does someone have an idea, which is the best way to accomplish this in an elegant way? Thank you very much!
Iterate over each and populate the array with values if present:
$combined = [];
foreach ($reference as $key => $val) {
$combined[$key] = [
'test' => 0,
'reference' => $val,
];
}
foreach ($test as $key => $val) {
if (!isset($combined[$key])) {
$combined[$key] = [
'reference' => 0,
'test' => 0,
]
}
$combined[$key]['test'] = $val;
}
$combined will contain both values from both arrays with reference to both the elements from $reference and $test.
try
$result = array_diff($reference, $test);
print_r($result)
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]
]
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
)
)
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>";
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);
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);
Let's say I have one array of ID numbers in a desired particular order. I have a second array of objects that were sorted by an ID property from the first array.
$array1 = [3,4,2,1,5];
$array2 = [
["id" => 1, "data" => "one"],
["id" => 2, "data" => "two"],
["id" => 3, "data" => "fre"],
["id" => 4, "data" => "foe"],
["id" => 5, "data" => "fie"]
];
In PHP, what is the best way of 'unsorting' or reverting the second array to the original order of the first array?
The closest answer I can find without using a sort is:
$array1_flipped = array_flip($array1);
$array2_unsorted = array();
for($i = 0; $i < count($array1); $i++) {
$array2_unsorted[$array1_flipped[$array2[$i]['id']]] = $array2[$i];
}
return($array2_unsorted);
Edit: For those interested, here is how the question arose. The first array is a list of IDs to be displayed in a particular order. The second array is the return of the MySQL call WHERE id IN $array2, which is returned sorted. However, the second array needs to be resorted back into the order of the first array. Due to size issues, I was hoping to be able to remap the second array using the keys and values of the first array without sorting.
I found the solution by introducing a third array and using a method similar to Gauss-Jordan elimination. While this is beautiful, I wish there was a one-step algorithm for this. I'll award the correct answer to anyone who finds it.
$array1 = [3,4,2,1,5];
$array2 = [
["id" => 1, "data" => "one"],
["id" => 2, "data" => "two"],
["id" => 3, "data" => "fre"],
["id" => 4, "data" => "foe"],
["id" => 5, "data" => "fie"]
];
// Placeholder sorted ascending array (e.g. $array3 = [1,2,3,4,5])
$array3 = range(1,count($array1));
array_multisort($array1, $array3);
// Now $array3 = [4,3,1,2,5], the inverse map of an $array1 sort
array_multisort($array3, $array2);
return $array2;
usort with an anonymous function receiving the order array via the use keyword:
$order = [3,4,2,1,5];
$ar = [
["id" => 1, "data" => "one"],
["id" => 2, "data" => "two"],
["id" => 3, "data" => "fre"],
["id" => 4, "data" => "foe"],
["id" => 5, "data" => "fie"]
];
usort($ar, function($a, $b) use ($order) {
$ai = array_search($a['id'], $order);
$bi = array_search($b['id'], $order);
if($ai == $bi) return 0;
return ($ai < $bi) ? -1 : 1;
});
print_r($ar);
I have an array like below.
$a = array(
array("id" => 1, "name" => "njx", "count" => 0),
array("id" => 2, "name" => "peter", "count" => 4),
array("id" => 3, "name" => "jeffry", "count" => 2),
array("id" => 4, "name" => "adam", "count" => 6)
);
and applied filter like this.
$fa = array_filter($a, function ($item) {
return ($item["count"] > 0);
});
then i applied usort on variable $fa. After that i loop through $fa and assigned some values, but they are not get reflected in variable $a.
something like below,
usort($fa, 'comp');
foreach ($fa as &$t) {
$t["score"] = 120;
}
var_dump($a); //this doesn't contain "score" field.
So my questions is how to get filtered array with original array reference?
array_filter returns a new array and not a reference, thats why any changes applied to $fa wont be reflected in $a.
Instead of using array_filter you could use a foreach loop like this:
foreach($a as &$t) {
if($t['count'] > 0) {
$t['score'] = 120;
}
}
And then sort using usort.