PHP recreate array if it contains certain values - php

I have $fruits_arr:
Array
(
[0] => Array
(
[id] => 213
[fruit] => banana
)
[1] => Array
(
[id] => 438
[fruit] => apple
)
[2] => Array
(
[id] => 154
[fruit] => peach
)
)
And $ids_arr:
Array (
[0] => 213
[1] => 154
)
I want to recreate $fruits_arr to have only array items where id is equal to a value from $ids_arr. I also want to maintain the index/array order of $fruits_arr.
I'm using the following:
$selected_fruits = array();
foreach( $fruits_arr as $fruit ) :
if ( in_array( $fruit['id'], $ids_arr ) ) :
$selected_fruits[] = $fruit;
endif;
endforeach;
print_r( $selected_fruits );
It seems to work but I am wondering if there is a shorter, better way to accomplish this in the latest PHP version.

This is not a shorter or newer way, but perhaps instead of performing in_array for every iteration, you could use array_flip and then use isset to check for the key:
$ids_arr = array_flip($ids_arr);
$selected_fruits = [];
foreach ($fruits_arr as $k => $fruit) {
if (isset($ids_arr[$fruit["id"]])) {
$selected_fruits[$k] = $fruit;
}
}
Php demo

You could use array_filter to make it more concise, but it will not be much and could make your code less readable. If possible chose readability over length of code.
$selected_fruits = array_filter($fruits_arr, function ($el) use ($ids_arr) {
return in_array($el['id'], $ids_arr, true);
});
or you can wait for PHP 7.4 (due to come out at the end of the year) and use arrow functions.
$selected_fruits = array_filter($fruits_arr, fn ($el) => in_array($el['id'], $ids_arr, true));
Demo: https://3v4l.org/4UC41

If the array is associative (use array_column for that) then you can use array_intersect_key to do the job.
$fruits_arr = array_column($fruits_arr, Null, "id");
$result = array_intersect_key($fruits_arr, array_flip($ids_arr));
var_dump($result);
https://3v4l.org/jQo6E

Keeping it simple, a loop where you remove items that are not in your keep array.
<?php
$fruits =
[
'foo' => [
'id' => 1,
'fruit' => 'banana'
],
'bar' => [
'id' => 2,
'fruit' => 'orange'
],
'baz' => [
'id' => 3,
'fruit' => 'pear'
],
];
$keep_ids = [1,3];
foreach($fruits as $k=>$v)
if(!in_array($v['id'], $keep_ids))
unset($fruits[$k]);
var_export($fruits);
Output:
array (
'foo' =>
array (
'id' => 1,
'fruit' => 'banana',
),
'baz' =>
array (
'id' => 3,
'fruit' => 'pear',
),
)

Related

Finding value from another one in an array of arrays

I got the follwing array and I would like to retrieve the name by the id:
Array
(
[0] => Array
(
[id] => 1
[name] => john
)
[1] => Array
(
[id] => 2
[name] => mark
)
etc...
It is doable with double foreach loop and a conditional test, but is there a more elegant way?
Assuming that id is unique...
Long Version
$arr = [
['id'=1, 'name'='john'],
['id'=2, 'name'='mark'],
];
$lookup = [];
foreach($arr as $row) {
$id = $row['id'];
$name = $row['name'];
$lookup[$id] = $name;
}
// find name for id, 2
echo $lookup[2];
// ==> mark
Short Version
...see Progrock’s solution!
You can use array_column to map ids to names:
<?php
$arr = [
['id' => 1, 'name' => 'Rolf'],
['id' => 3, 'name' => 'Gary'],
['id' => 2, 'name' => 'Jimmy'],
];
$id_names = array_column($arr, 'name', 'id');
var_export($id_names);
print $id_names[3];
Output:
array (
1 => 'Rolf',
3 => 'Gary',
2 => 'Jimmy',
)Gary

Remove the first element (key and value) from an array, turning it into another array

I want to capture the first element of an array and its value in a second array, removing it from the first.
Is there a core PHP function that does what my_function does here?
function my_function(&$array) {
$key = current(array_keys($array));
$value = $array[$key];
unset($array[$key]);
return [$key => $value];
}
$array = [
'color' => 'red',
'car' => 'ford'
'house' => 'cottage',
];
$top = my_function($array);
print_r($top);
print_r($array);
Output:
Array
(
[color] => red
)
Array
(
[car] => ford
[house] => cottage
)
If there's not a core function, is there a simpler way of achieving this behavior? IIRC, passing variables by reference is frowned upon.
Bonus question: is there a word for the combination of both key and element from an array? I feel like 'element' doesn't necessarily include the key.
edit Since there seems to be a commonly held misunderstanding, at least in PHP 7, array_shift does not do the desired behavior. It only returns the first value, not the first element:
$ cat first_element.php
<?php
$array = [
'color' => 'red',
'car' => 'ford',
'house' => 'cottage',
];
$top = array_shift($array);
print_r($top);
print_r($array);
$ php first_element.php
redArray
(
[car] => ford
[house] => cottage
)
Try this (array_splice):
$top = array_splice($array, 0, 1);
The $top will contain the first element and the $array will contain the rest of the elements.
array_splice doesn't always preserve keys, so just get the key and combine with the result of array_shift to also remove it:
$result = [key($array) => array_shift($array)];
If needed, reset the array pointer:
reset($array) && $result = [key($array) => array_shift($array)];
function my_function($array) {
$first_key = key($array);
return array($first_key =>$array[$first_key] );
}
$array = array( 'color' => 'red', 'car' => 'ford','house' => 'cottage' );
$first = my_function($array);
array_shift($array);print_r($first);print_r($array);
I made this function:
function array_extract(array &$array, $num) {
$output = array_slice($array,0, $num);
array_splice($array,0, $num);
return $output;
}
Here's what it does
$ cat test.php
<?php
$test = [234,25,45,78,56];
echo "test:\n";
print_r($test);
while( count($test) ) {
echo "extraction:\n";
print_r(array_extract($test, 2));
echo "\ntest:\n";
print_r($test);
}
$ php test.php
test:
Array
(
[0] => 234
[1] => 25
[2] => 45
[3] => 78
[4] => 56
)
extraction:
Array
(
[0] => 234
[1] => 25
)
test:
Array
(
[0] => 45
[1] => 78
[2] => 56
)
extraction:
Array
(
[0] => 45
[1] => 78
)
test:
Array
(
[0] => 56
)
extraction:
Array
(
[0] => 56
)
test:
Array
(
)
Quite simple:
$array1 = [
'color' => 'red',
'car' => 'ford'
'house' => 'cottage',
];
$array2 = array_unshift($array1);
--> result
$array2 = [
'color' => 'red'
];
$array1 = [
'car' => 'ford'
'house' => 'cottage',
];

How to add new index and value from another array in PHP?

I need your help with my problem. My problem is I have 2 arrays the first one is the main array. The second is the array for my new data.
Let's say I have these arrays.
This is the main array:
Array
(
0 => Array
(
'id' => 1,
'name' => 'Apple',
'age' => 12
)
1 => Array
(
'id' => 2,
'name' => May,
'age' => 13
)
)
This is the second array:
Array
(
1 => Array
(
'gender' => 'Male'
)
2 => Array
(
'gender' => 'Female'
)
)
And I have this loop in PHP
foreach($main_array as &$main){
//this is the loop inside the first array
// how can I add the second array with it?
}
This is the sample output:
[0] => Array
(
[id] => 1
[name] => Apple
[age] => 12
[gender] => Female
)
[1] => Array
(
[id] => 2
[name] => May
[age] => 13
[gender] => Female
)
How can I do that? Any suggestions? That's all thanks.
for($i=0; $i<count($main_array); $i++){
for($j=0; $j<count($second_array); $j++){
if($main_array[$i]['id'] == $j){
$main_array[$i]['gender'] = $second_array[$j]['gender']
}
}
}
I fixed your example code, it wont run otherwise.
<?php
// Test data:
$main_array = Array(
0 => Array(
'id' => 1,
'name' => 'Apple',
'age' => 12
),
1 => Array (
'id' => 2,
'name' => 'May',
'age' => 13
)
);
$lookup = Array(
1 => Array(
'gender' => 'Male'
),
2 => Array(
'gender' => 'Female'
)
);
// Your answer:
foreach ($main_array as &$main) {
if (array_key_exists($main['id'],$lookup)) {
$main['gender'] = $lookup[$main['id']]['gender']; // <-- sets gender value
}
}
// Output it to browser:
echo '<pre>$main_array = '.print_r($main_array,true).'</pre>';
The array_key_exists() check is there to avoid errors such as PHP Notice: Undefined offset: 123 when the $lookup data is incomplete.
If you want to merge all of the data from both arrays:
PHP tools:
The exact behaviors of these functions needs to be studied and tested before usage, to make sure it fits your intent.
// array merge recursive doesn't merge numeric keys
$main_array = array_merge_recursive($main_array, $secondary_array);
// array replace recursive has a downside of replacing stuff
$main_array = array_replace_recursive($main_array, $secondary_array);
Rolling your own:
foreach($main_array as $i => &$main){
if(isset($secondary_array[$i])) {
foreach($secondary_array[$i] AS $key => $value) {
$main[$key] = $value;
}
}
}
Both of the above solutions only apply if the array-indexes of $main_array and $secondary_array match.
In your example your arrays don't match:
- $secondary_array[0] doesn't exist so $main_array[0] will not be populated with a 'gender' value;
- $main_array[2] doesn't exist so $main_array[2] will be created and it will only have a 'gender' value same as $secondary_array[2]['gender']
If you want to only merge some bits and pieces of the arrays:
Rolling your own:
foreach($main_array as $i => &$main) {
if(isset($secondary_array[$i])) and isset($secondary_array[$i]['gender'])) {
$main['gender'] = $secondary_array[$i]['gender'];
}
}
foreach($main_array as &$main){//this is the loop inside the first array
foreach($second_array as &$second){ //this is the loop inside the second array
}
}
foreach($arr1 as $k => $arr1Item) {
$arr1[$k]['gender'] = $arr2[$k]['gender'];
}

How do you reindex an array in PHP but with indexes starting from 1?

I have the following array, which I would like to reindex so the keys are reversed (ideally starting at 1):
Current array (edit: the array actually looks like this):
Array (
[2] => Object
(
[title] => Section
[linked] => 1
)
[1] => Object
(
[title] => Sub-Section
[linked] => 1
)
[0] => Object
(
[title] => Sub-Sub-Section
[linked] =>
)
)
How it should be:
Array (
[1] => Object
(
[title] => Section
[linked] => 1
)
[2] => Object
(
[title] => Sub-Section
[linked] => 1
)
[3] => Object
(
[title] => Sub-Sub-Section
[linked] =>
)
)
If you want to re-index starting to zero, simply do the following:
$iZero = array_values($arr);
If you need it to start at one, then use the following:
$iOne = array_combine(range(1, count($arr)), array_values($arr));
Here are the manual pages for the functions used:
array_values()
array_combine()
range()
Why reindexing? Just add 1 to the index:
foreach ($array as $key => $val) {
echo $key + 1, '<br>';
}
Edit   After the question has been clarified: You could use the array_values to reset the index starting at 0. Then you could use the algorithm above if you just want printed elements to start at 1.
This will do what you want:
<?php
$array = array(2 => 'a', 1 => 'b', 0 => 'c');
array_unshift($array, false); // Add to the start of the array
$array = array_values($array); // Re-number
// Remove the first index so we start at 1
$array = array_slice($array, 1, count($array), true);
print_r($array); // Array ( [1] => a [2] => b [3] => c )
?>
You may want to consider why you want to use a 1-based array at all. Zero-based arrays (when using non-associative arrays) are pretty standard, and if you're wanting to output to a UI, most would handle the solution by just increasing the integer upon output to the UI.
Think about consistency—both in your application and in the code you work with—when thinking about 1-based indexers for arrays.
You can reindex an array so the new array starts with an index of 1 like this;
$arr = array(
'2' => 'red',
'1' => 'green',
'0' => 'blue',
);
$arr1 = array_values($arr); // Reindex the array starting from 0.
array_unshift($arr1, ''); // Prepend a dummy element to the start of the array.
unset($arr1[0]); // Kill the dummy element.
print_r($arr);
print_r($arr1);
The output from the above is;
Array
(
[2] => red
[1] => green
[0] => blue
)
Array
(
[1] => red
[2] => green
[3] => blue
)
Well, I would like to think that for whatever your end goal is, you wouldn't actually need to modify the array to be 1-based as opposed to 0-based, but could instead handle it at iteration time like Gumbo posted.
However, to answer your question, this function should convert any array into a 1-based version
function convertToOneBased( $arr )
{
return array_combine( range( 1, count( $arr ) ), array_values( $arr ) );
}
EDIT
Here's a more reusable/flexible function, should you desire it
$arr = array( 'a', 'b', 'c' );
echo '<pre>';
print_r( reIndexArray( $arr ) );
print_r( reIndexArray( $arr, 1 ) );
print_r( reIndexArray( $arr, 2 ) );
print_r( reIndexArray( $arr, 10 ) );
print_r( reIndexArray( $arr, -10 ) );
echo '</pre>';
function reIndexArray( $arr, $startAt=0 )
{
return ( 0 == $startAt )
? array_values( $arr )
: array_combine( range( $startAt, count( $arr ) + ( $startAt - 1 ) ), array_values( $arr ) );
}
A more elegant solution:
$list = array_combine(range(1, count($list)), array_values($list));
The fastest way I can think of
array_unshift($arr, null);
unset($arr[0]);
print_r($arr);
And if you just want to reindex the array(start at zero) and you have PHP +7.3 you can do it this way
array_unshift($arr);
I believe array_unshift is better than array_values as the former does not create a copy of the array.
Changelog
Version
Description
7.3.0
This function can now be called with only one parameter. Formerly, at least two parameters have been required.
-- https://www.php.net/manual/en/function.array-unshift.php#refsect1-function.array-unshift-changelog
$tmp = array();
foreach (array_values($array) as $key => $value) {
$tmp[$key+1] = $value;
}
$array = $tmp;
It feels like all of the array_combine() answers are all copying the same "mistake" (the unnecessary call of array_values()).
array_combine() ignores the keys of both parameters that it receives.
Code: (Demo)
$array = [
2 => (object)['title' => 'Section', 'linked' => 1],
1 => (object)['title' => 'Sub-Section', 'linked' => 1],
0 => (object)['title' => 'Sub-Sub-Section', 'linked' => null]
];
var_export(array_combine(range(1, count($array)), $array));
Output:
array (
1 =>
(object) array(
'title' => 'Section',
'linked' => 1,
),
2 =>
(object) array(
'title' => 'Sub-Section',
'linked' => 1,
),
3 =>
(object) array(
'title' => 'Sub-Sub-Section',
'linked' => NULL,
),
)
If you are not trying to reorder the array you can just do:
$array = array_reverse( $array );
and then call it once more to get it back to the same order:
$array = array_reverse( $array );
array_reverse() reindexes as it reverses. Someone else showed me this a long time ago. So I can't take credit for coming up with it. But it is very simple and fast.
The result is an array with indexed keys starting from 0. https://3v4l.org/iQgVh
array (
0 =>
(object) array(
'title' => 'Section',
'linked' => 1,
),
1 =>
(object) array(
'title' => 'Sub-Section',
'linked' => 1,
),
2 =>
(object) array(
'title' => 'Sub-Sub-Section',
'linked' => NULL,
),
)
Here's my own implementation. Keys in the input array will be renumbered with incrementing keys starting from $start_index.
function array_reindex($array, $start_index)
{
$array = array_values($array);
$zeros_array = array_fill(0, $start_index, null);
return array_slice(array_merge($zeros_array, $array), $start_index, null, true);
}

How can I merge PHP arrays?

I have two arrays of animals (for example).
$array = array(
array(
'id' => 1,
'name' => 'Cat',
),
array(
'id' => 2,
'name' => 'Mouse',
)
);
$array2 = array(
array(
'id' => 2,
'age' => 321,
),
array(
'id' => 1,
'age' => 123,
)
);
How can I merge the two arrays into one by the ID?
#Andy
http://se.php.net/array_merge
That was my first thought but it doesn't quite work - however array_merge_recursive might work - too lazy to check right now.
This does what Erik suggested (id no. as array key) and merges vlaues in $array2 to $results.
$results = array();
foreach($array as $subarray)
{
$results[$subarray['id']] = array('name' => $subarray['name']);
}
foreach($array2 as $subarray)
{
if(array_key_exists($subarray['id'], $results))
{
// Loop through $subarray would go here if you have extra
$results[$subarray['id']]['age'] = $subarray['age'];
}
}
First off, why don't you use the ID as the index (or key, in the mapping-style array that php arrays are imo)?
$array = array(
1 => array(
'name' => 'Cat',
),
2 => array(
'name' => 'Mouse',
)
);
after that you'll have to foreach through one array, performing array_merge on the items of the other:
foreach($array2 as $key=>$value) {
if(!is_array($array[$key])) $array[$key] = $value;
else $array[$key] = array_merge($array[key], $value);
}
Something like that at least. Perhaps there's a better solution?
<?php
$a = array('a' => '1', 'b' => array('t' => '4', 'g' => array('e' => '8')));
$b = array('c' => '3', 'b' => array('0' => '4', 'g' => array('h' => '5', 'v' => '9')));
$c = array_merge_recursive($a, $b);
print_r($c);
?>
array_merge_recursive — Merge two or more arrays recursively
outputs:
Array
(
[a] => 1
[b] => Array
(
[t] => 4
[g] => Array
(
[e] => 8
[h] => 5
[v] => 9
)
[0] => 4
)
[c] => 3
)
#Andy
I've already looked at that and didn't see how it can help merge multidimensional arrays. Maybe you could give an example.
#kevin
That is probably what I will need to do as I think the code below will be very slow.
The actual code is a bit different because I'm using ADOdb (and ODBC for the other query) but I'll make it work and post my own answer.
This works, however I think it will be very slow as it goes through the second loop every time:
foreach($array as &$animal)
{
foreach($array2 as $animal2)
{
if($animal['id'] === $animal2['id'])
{
$animal = array_merge($animal, $animal2);
break;
}
}
}
foreach ($array as $a)
$new_array[$a['id']]['name'] = $a['name'];
foreach ($array2 as $a)
$new_array[$a['id']]['age'] = $a['age'];
and this is result:
[1] => Array
(
[name] => Cat
[age] => 123
)
[2] => Array
(
[name] => Mouse
[age] => 321
)
<?php
$array1 = array("color" => "red", 2, 4);
$array2 = array("a", "b", "color" => "green", "shape" => "trapezoid", 4);
$result = array_merge($array1, $array2);
print_r($result);
?>
With PHP 5.3 you can do this sort of merge with array_replace_recursive()
http://www.php.net/manual/en/function.array-replace-recursive.php
You're resultant array should look like:
Array (
[0] => Array
(
[id] => 2
[name] => Cat
[age] => 321
)
[1] => Array
(
[id] => 1
[name] => Mouse
[age] => 123
)
)
Which is what I think you wanted as a result.
I would rather prefer array_splice over array_merge because of its performance issues, my solution would be:
<?php
array_splice($array1,count($array1),0,$array2);
?>
$new = array();
foreach ($array as $arr) {
$match = false;
foreach ($array2 as $arr2) {
if ($arr['id'] == $arr2['id']) {
$match = true;
$new[] = array_merge($arr, $arr2);
break;
}
}
if ( !$match ) $new[] = $arr;
}

Categories