Remove from 2d array if it belongs in array - php

I'm trying to remove arrays in the cars array if the make value belongs in the retiredCars array. I did run into array_search but I'm not sure how to apply it to a multi dimensional array
$retiredCars = array("Saab", "Saturn", "Pontiac");
$cars = array
(
array('make' => 'BMW', 'model' => '325'),
array('make' => 'Saab', 'model' => '93'),
array('make' => 'Pontiac', 'model' => 'GTO')
);
In the example above, the $cars array should only contain the 'BMW' array after processing

foreach ($cars as $key => $arr) {
if (in_array($arr['make'], $retiredCars))
unset($cars[$key]);
}

Unsetting array's elements while iterating over it doesn't seem a good approach. Another idea:
$array_elem_passes = function ($val) use ($retiredCars)
{
if (in_array($val['make'], $retiredCars))
return false;
return true;
};
$ret = array_filter($cars, $array_elem_passes);

You can use array_filter, for performance flip the retiredCars array. Live demo
You also can use array_udiff to make it. Refer to this post.
<?php
$retiredCars = array("Saab", "Saturn", "Pontiac");
$cars = array
(
array('make' => 'BMW', 'model' => '325'),
array('make' => 'Saab', 'model' => '93'),
array('make' => 'Pontiac', 'model' => 'GTO')
);
$makes = array_flip(array_unique($retiredCars));
print_r(array_filter($cars, function($v)use($makes){return isset($makes[$v['make']]);}));

Yes. array_udiff do the trick in this way
$res = array_udiff($cars, $retiredCars,
function($c, $r) {
// You need test both variable because udiff function compare them in all combinations
$a = is_array($c) ? $c['make'] : $c;
$b = is_array($r) ? $r['make'] : $r;
return strcmp($a, $b);
});
print_r($res);
demo on eval.in

Related

Slick way to combine two arrays by value

I have a situation where I have one array like this:
//[itemId] => [agentId]
[123] => [1qa2ws]
[456] => [3ed4rf]
And other:
// array of agents with full objects inside
[0] => [id => 1qa2ws, ....]
[1] => [id => 3ed4rf, ....]
So now I would like to combine this in a way that I can tell "okay, if ID from the agent array is the same as the value of the first array, apply the whole object to it instead of what now is only the ID".
I have a 'dirty' solution:
foreach ($agentIDs as &$agentID){
foreach ($resolvedAgents as $agent){
if($agent['accountId'] == $agentID){
$agentID = $agent;
}
}
}
The order of two arrays doesn't have to be the same, so the first array position of the first array isn't necessarily the same agent as the first agent in the agent array.
I would like to end up with:
[123] => [id => 1qa2ws, ....]
[456] => [id => 3ed4rf, ....]
Is there some cleaner solution to it?
What about array_combine?
$result = array_combine($array1, $array2)
PHPDoc
You could use array_reduce:
$a = array();
$a[123] = ['1qa2ws'];
$a[456] = ['3ed4rf'];
$b = array();
$b[0] = ['id' => '1qa2ws', 'other' => 'data'];
$b[1] = ['id' => '3ed4rf', 'other' => 'data'];
$b[2] = ['id' => 'av42sa', 'other' => 'data'];
$c = array_reduce($a, function ($carry, $item) use ($a, $b) {
$itemId = array_search($item, $a);
$agentId = array_values($item)[0];
if (array_search($agentId, array_column($b, 'id')) !== false) {
$carry[$itemId] = $b[array_search($agentId, array_column($b, 'id'))];
}
return $carry;
}, []);
I think u can use array_combine and array_column:
$resolvedAgents = array_combine(array_column($resolvedAgents, 'id'), $resolvedAgents);
After that $resolvedAgents will become:
[1qa2ws] => [id => 1qa2ws, ....]
[3ed4rf] => [id => 3ed4rf, ....]
And also u will have your first array:
// [itemId] => [agentId]
[123] => [1qa2ws]
[456] => [3ed4rf]
If u need just a single array at the the end then:
$agents = array_map(function($agentId) use ($resolvedAgents) {
return $resolvedAgents[$agentId] ?: null;
}, $agentIDs);
At the end your code will be:
$resolvedAgents = array_combine(array_column($resolvedAgents, 'id'), $resolvedAgents);
$agents = array_map(function($agentId) use ($resolvedAgents) {
return $resolvedAgents[$agentId] ?: null;
}, $agentIDs);
I actually ended up doing this, which is the solution I am happy with:
$agents = array_map(function($agent) use ($resolvedAgents){
return $resolvedAgents[array_search($agent, array_column($resolvedAgents, 'accountId'))] ?: null;
}, $agentIDs);
Basically returning item from the second array from the position where it is found.

Remove array items based on sub array duplicate values and then sort in custom order

Remove array items based on duplicate values that appear 1 level deeper inside the array. Once the items have been sorted of duplicates, then would be cool to re order the new array.
This is the current input array...
$downloads = [
['type' => 'PHOTOS'],
['type' => 'DOCUMENTS'],
['type' => 'DOCUMENTS'],
['type' => 'VIDEOS'],
['type' => 'PHOTOS'],
];
I would like to remove all duplicates from this input so I am left with this new output...
[
['type' => 'PHOTOS'],
['type' => 'DOCUMENTS'],
['type' => 'VIDEOS'],
]
But is it possible to set and ordering to each TYPE value. For example can I set predetermined orders using a variables or something. Any advice on re-ordering the new array to a specific order. Using this new order...
$photos = 1;
$videos = 2;
$documents = 3;
or a new order using an array maybe...
$new_order = array(
1 => 'PHOTOS',
2 => 'VIDEOS',
3 => 'DOCUMENTS'
)
Any help would be so good. I've tried array_unique and array_map but I can't seem to find out how to specify which sub array key to check for duplicates.
This is what i've tried so far...
$downloads = get_field('downloads');
$types = array_unique($downloads));
and
$downloads = get_field('downloads');
$types = array_map("unserialize", array_unique(array_map("serialize", $downloads)));
I didn't get as far as re ordering the array.
The solution using array_column, array_map, array_search and usort functions:
$new_order = array(
0 => 'PHOTOS',
1 => 'VIDEOS',
2 => 'DOCUMENTS'
);
// $downloads is your input array
$types = array_map(function ($v) {
return ['TYPE' => $v];
},array_unique(array_column($downloads, 'TYPE')));
usort($types, function($a, $b) use($new_order){
$a_key = array_search($a['TYPE'], $new_order);
$b_key = array_search($b['TYPE'], $new_order);
if ($a_key == $b_key) return 0;
return ($a_key < $b_key)? -1 : 1;
});
print_r($types);
The output:
Array
(
[0] => Array
(
[TYPE] => PHOTOS
)
[1] => Array
(
[TYPE] => VIDEOS
)
[2] => Array
(
[TYPE] => DOCUMENTS
)
)
Quick and dirty solution for removing "subarray" duplicates:
$a = [];
$a[]['type'] = 'photos';
$a[]['type'] = 'documents';
$a[]['type'] = 'documents';
$a[]['type'] = 'videos';
$a[]['type'] = 'photos';
$b = [];
foreach( $a as $index => $subA ) {
if( in_array($subA['type'], $b) ) {
unset($a[$index]);
} else {
$b[] = $subA['type'];
}
}
Regarding the sorting: Just set the indices with the order you need them and after removing the duplicates use ksort (assuming I did understand you right).
Simply use array_intersect that computes common part of two arrays. If you use order array as first argument, order of that will be preserved.
Your input:
$input = [
['TYPE' => 'PHOTOS'],
['TYPE' => 'DOCUMENTS'],
['TYPE' => 'DOCUMENTS'],
['TYPE' => 'VIDEOS'],
['TYPE' => 'PHOTOS']
];
I've write simple function to achieve all of your needs:
function do_awesomness($input, $key = 'TYPE', $order = ['PHOTOS', 'VIDEOS', 'DOCUMENTS'])
{
$result = [];
foreach($input as $k => $value)
$result[] = $value[$key];
$result = array_unique($result);
return array_values(array_intersect($order, $result));
}
Usage:
do_awesomness($input);
Working example: http://phpio.net/s/1lo1
You can use array_reduce to reimplement array_unique, but collecting unique types in order of appearance. This will help us to sort array later.
$order = [
'PHOTOS' => 1,
'VIDEOS' => 2,
'DOCUMENTS' => 3
];
$types = [];
$uniqueDownloads = array_reduce(
$downloads,
function ($uniqueDownloads, $download) use (&$types, $order) {
$type = $download['TYPE'];
if (!isset($types[$type])) {
$types[$type] = isset($order[$type]) ? $order[$type] : 0;
$uniqueDownloads[] = $download;
}
return $uniqueDownloads;
},
[]
);
array_multisort($types, $uniqueDownloads);
Traversing the array we check whether the type key exists in $types. If it exists skip this element. Otherwise, set this key and assign value from $order to it (this will be used for sorting).
We then use array_multisort to sort $uniqueDownloads by sorting $types.
Here is working demo.
Read more about anonymous functions. I have passed $types by reference with & so the function change value of the original array, but not the value of the copy.
For custom ordering I recommended usort:
$array = [
['TYPE'=>'PHOTOS'],
['TYPE'=>'DOCUMENTS'],
['TYPE'=>'VIDEOS']
];
function customOrder($a, $b){
$newOrder = [
'PHOTOS' => 1,
'VIDEOS' => 2,
'DOCUMENTS' => 3
];
$valA = (array_key_exists($a['TYPE'],$newOrder))?$newOrder[$a['TYPE']]:999;
$valB = (array_key_exists($b['TYPE'],$newOrder))?$newOrder[$b['TYPE']]:999;
if($valA > $valB){
$result = 1;
} elseif($valA < $valB){
$result = -1;
} else {
$result = 0;
}
return $result;
}
usort($array, "customOrder");
var_dump($array);
Because your ordering array appears to be an exhaustive list of possible download values, you can just filter that static array by the downloads values.
Code: (Demo)
var_export(
array_intersect(
$new_order,
array_column($downloads, 'type')
)
);
If there may be values in the downloads array that are not represented in the ordering array, then the sample data in the question should be adjusted to better represent the application data.

PHP Remove Multidimensional Array Value

I have array multidimensional code like this:
$array = [
'fruits' => ['apple','orange','grape', 'pineaple'],
'vegetables' => ['tomato', 'potato']
];
$eaten = 'grape';
unset($array[$eaten]);
and what i need is to delete 'grape' from the array because 'grape' already eaten. how to fix my code to unset the 'grape'?
and my question number two, if it can be unset, is there a way to unset multi value like
unset($array,['grape','orange']);
thanks for help..
You can remove eaten element by following way. Use array_search() you can find key at the position of your eaten element.
Here below code shows that in any multidimensional array you can call given function.
$array = [
'fruits' => ['apple','orange','grape', 'pineaple'],
'vegetables' => ['tomato', 'potato']
];
$eaten = 'grape';
$array = removeElement($array, $eaten);
function removeElement($data_arr, $eaten)
{
foreach($data_arr as $k => $single)
{
if (count($single) != count($single, COUNT_RECURSIVE))
{
$data_arr[$k] = removeElement($single, $eaten);
}
else
{
if(($key = array_search($eaten, $single)) !== false)
{
unset($data_arr[$k][$key]);
}
}
}
return $data_arr;
}
P.S. Please note that you can unset() multiple elements in single call. But the way you are using unset is wrong.
Instead of using unset() i suggest you to create a new Array after removal of required value benefit is that, your original array will remain same, you can use it further:
Example:
// your array
$yourArr = array(
'fruits'=>array('apple','orange','grape', 'pineaple'),
'vegetables'=>array('tomato', 'potato')
);
// remove array that you need
$removeArr = array('grape','tomato');
$newArr = array();
foreach ($yourArr as $key => $value) {
foreach ($value as $finalVal) {
if(!in_array($finalVal, $removeArr)){ // check if available in removal array
$newArr[$key][] = $finalVal;
}
}
}
echo "<pre>";
print_r($newArr);
Result:
Array
(
[fruits] => Array
(
[0] => apple
[1] => orange
[2] => pineaple
)
[vegetables] => Array
(
[0] => potato
)
)
Explanation:
Using this array array('grape','tomato'); which will remove the value that you define in this array.
This is how I would do it.
$array = [
'fruits' => ['apple','orange','grape', 'pineaple'],
'vegetables' => ['tomato', 'potato']
];
$unset_item = 'grape';
$array = array_map(function($items) use ($unset_item) {
$found = array_search($unset_item, $items);
if($found){
unset($items[$found]);
}
return $items;
}, $array);

PHP-Sort array based on another array?

OK, I already got this question in stackoverflow but sadly it's in javascript - Javascript - sort array based on another array
and I want it in PHP
$data = array(
"item1"=>"1",
"item2"=>"3",
"item3"=>"5",
"item4"=>"2",
"item5"=>"4"
);
to match the arrangement of this array:
sortingArr = array("5","4","3","2","1");
and the output I'm looking for:
$data = array(
"item3"=>"5",
"item5"=>"4",
"item2"=>"3",
"item4"=>"2",
"item1"=>"1"
);
Any idea how this can be done?
Thanks.
For a detailed answer, why array_multisort does not match your needs, view this answer, please:
PHP array_multisort not sorting my multidimensional array as expected
In short: You want to sort an array based on a predefined order. The Answer is also given over there, but i copied one solution to this answer, too:
Use usort and array_flip, so you be able to turn your indexing array (ValueByPosition) into a PositionByValue Array.
$data = array(
"item1"=>"1",
"item2"=>"3",
"item3"=>"5",
"item4"=>"2",
"item5"=>"4"
);
usort($data, "sortByPredefinedOrder");
function sortByPredefinedOrder($leftItem, $rightItem){
$order = array("5","4","3","2","1");
$flipped = array_flip($order);
$leftPos = $flipped[$leftItem];
$rightPos = $flipped[$rightItem];
return $leftPos >= $rightPos;
}
print_r($data);
// usort: Array ( [0] => 5 [1] => 4 [2] => 3 [3] => 2 [4] => 1 )
// uasort: Array ( [item3] => 5 [item5] => 4 [item2] => 3 [item4] => 2 [item1] => 1 )
However this would require you to predict all possible items inside the predefined order array, or thread other items in an appropriate way.
If you want to maintain the assoc keys, use uasort instead of usort.
Pretty simple ?
$data = array(
"item1"=>"1",
"item2"=>"3",
"item3"=>"5",
"item4"=>"2",
"item5"=>"4"
);
$sortingArr = array("5","4","3","2","1");
$result = array(); // result array
foreach($sortingArr as $val){ // loop
$result[array_search($val, $data)] = $val; // adding values
}
print_r($result); // print results
Output:
Array
(
[item3] => 5
[item5] => 4
[item2] => 3
[item4] => 2
[item1] => 1
)
using usort() the right way i think
Sort an array by values using a user-defined comparison function
you can do as follow:
$data = array(
"item1"=>"1",
"item2"=>"3",
"item3"=>"5",
"item4"=>"2",
"item5"=>"4"
);
$sortingArr = array("5","4","3","2","1");
$keys = array_flip($sortingArr);
usort($data, function ($a, $b) use ($keys) {
return $keys[$a] > $keys[$b] ? 1 : -1;
});
print_r($data);
// Output
// Array ( [0] => 5 [1] => 4 [2] => 3 [3] => 2 [4] => 1 )
live example: https://3v4l.org/75cnu
Look at my following snippet to sort your array based on another array:
$res_arr = array();
for ($i = 0; $i < count($sortingArr); $i++) {
for ($j = 0; $j < count($data); $j++) {
if($data[$j] == $sortingArr[$i]) {
$res_arr[] = $data[$j];
break;
}
}
}
// $res_array is your sorted array now
Look at code snippet to make a multidimensional array sort in order of input
$input_format_list = [4, 1];
$data = array(
"0" => array(
"School" => array(
"id" => 1,
"name" => "ST.ANN'S HIGH SCHOOL",
)
),
"1" => array(
"School" => array(
"id" => 4,
"name" => "JYOTI VIDHYA VIHAR",
)
)
);
$result = array(); // result array
foreach($input_format_list as $key => $value){ // loop
foreach ($data as $k => $val) {
if ($data[$k]['School']['id'] === $value) {
$result[$key] = $data[$k];
}
}
}
return $result;
Take a look at array_multisort. I'm not completely sure how to use it, as I have never found a practical use for it (I prefer to use usort to clearly define my terms), but it might work for you.
<?php
$data = array(
"item1"=>"1",
"item2"=>"3",
"item3"=>"5",
"item4"=>"2",
"item5"=>"4"
);
$result=array_flip($data);
krsort($result);
$result=array_flip($result);
print_r($result);
//use rsort for the index array
$sortingArr = array("5","4","3","2","1");
print_r($sortingArr);
I'm pretty proud of my solution:
uasort($data, function($a, $b) use ($sortingArr) {
return array_search($a, $sortingArr) <=> array_search($b, $sortingArr);
});
Working example: https://3v4l.org/bbIk2
It uses uasort to maintain the key-value associations as the OP requested. (unlike #hassan's otherwise elegant solution)
It doesn't require that every element in the $data array be present in the sorting array. (like #HamZa's solution)
It's brief.
It uses the spaceship operator <=> for comparison instead of more verbose logic.
Code:
Expanding on the Answer of Andrew, if you want the undefined entries in the sorting array to appear at the end of the output array:
uasort($currentTags, function ($a, $b) use ($sortingArr) {
if (in_array($a, $sortingArr) && !in_array($b, $sortingArr)) return -1;
if (!in_array($a, $sortingArr) && in_array($b, $sortingArr)) return 1;
if (!in_array($b, $sortingArr)) return -1;
return array_search($a, $sortingArr) <=> array_search($b, $sortingArr);
});

Replace keys in an array based on another lookup/mapping array

I have an associative array in the form key => value where key is a numerical value, however it is not a sequential numerical value. The key is actually an ID number and the value is a count. This is fine for most instances, however I want a function that gets the human-readable name of the array and uses that for the key, without changing the value.
I didn't see a function that does this, but I'm assuming I need to provide the old key and new key (both of which I have) and transform the array. Is there an efficient way of doing this?
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
The way you would do this and preserve the ordering of the array is by putting the array keys into a separate array, find and replace the key in that array and then combine it back with the values.
Here is a function that does just that:
function change_key( $array, $old_key, $new_key ) {
if( ! array_key_exists( $old_key, $array ) )
return $array;
$keys = array_keys( $array );
$keys[ array_search( $old_key, $keys ) ] = $new_key;
return array_combine( $keys, $array );
}
if your array is built from a database query, you can change the key directly from the mysql statement:
instead of
"select ´id´ from ´tablename´..."
use something like:
"select ´id´ **as NEWNAME** from ´tablename´..."
The answer from KernelM is nice, but in order to avoid the issue raised by Greg in the comment (conflicting keys), using a new array would be safer
$newarr[$newkey] = $oldarr[$oldkey];
$oldarr=$newarr;
unset($newarr);
$array = [
'old1' => 1
'old2' => 2
];
$renameMap = [
'old1' => 'new1',
'old2' => 'new2'
];
$array = array_combine(array_map(function($el) use ($renameMap) {
return $renameMap[$el];
}, array_keys($array)), array_values($array));
/*
$array = [
'new1' => 1
'new2' => 2
];
*/
You could use a second associative array that maps human readable names to the id's. That would also provide a Many to 1 relationship. Then do something like this:
echo 'Widgets: ' . $data[$humanreadbleMapping['Widgets']];
If you want also the position of the new array key to be the same as the old one you can do this:
function change_array_key( $array, $old_key, $new_key) {
if(!is_array($array)){ print 'You must enter a array as a haystack!'; exit; }
if(!array_key_exists($old_key, $array)){
return $array;
}
$key_pos = array_search($old_key, array_keys($array));
$arr_before = array_slice($array, 0, $key_pos);
$arr_after = array_slice($array, $key_pos + 1);
$arr_renamed = array($new_key => $array[$old_key]);
return $arr_before + $arr_renamed + $arr_after;
}
Simple benchmark comparison of both solution.
Solution 1 Copy and remove (order lost, but way faster) https://stackoverflow.com/a/240676/1617857
<?php
$array = ['test' => 'value', ['etc...']];
$array['test2'] = $array['test'];
unset($array['test']);
Solution 2 Rename the key https://stackoverflow.com/a/21299719/1617857
<?php
$array = ['test' => 'value', ['etc...']];
$keys = array_keys( $array );
$keys[array_search('test', $keys, true)] = 'test2';
array_combine( $keys, $array );
Benchmark:
<?php
$array = ['test' => 'value', ['etc...']];
for ($i =0; $i < 100000000; $i++){
// Solution 1
}
for ($i =0; $i < 100000000; $i++){
// Solution 2
}
Results:
php solution1.php 6.33s user 0.02s system 99% cpu 6.356 total
php solution1.php 6.37s user 0.01s system 99% cpu 6.390 total
php solution2.php 12.14s user 0.01s system 99% cpu 12.164 total
php solution2.php 12.57s user 0.03s system 99% cpu 12.612 total
If your array is recursive you can use this function:
test this data:
$datos = array
(
'0' => array
(
'no' => 1,
'id_maquina' => 1,
'id_transaccion' => 1276316093,
'ultimo_cambio' => 'asdfsaf',
'fecha_ultimo_mantenimiento' => 1275804000,
'mecanico_ultimo_mantenimiento' =>'asdfas',
'fecha_ultima_reparacion' => 1275804000,
'mecanico_ultima_reparacion' => 'sadfasf',
'fecha_siguiente_mantenimiento' => 1275804000,
'fecha_ultima_falla' => 0,
'total_fallas' => 0,
),
'1' => array
(
'no' => 2,
'id_maquina' => 2,
'id_transaccion' => 1276494575,
'ultimo_cambio' => 'xx',
'fecha_ultimo_mantenimiento' => 1275372000,
'mecanico_ultimo_mantenimiento' => 'xx',
'fecha_ultima_reparacion' => 1275458400,
'mecanico_ultima_reparacion' => 'xx',
'fecha_siguiente_mantenimiento' => 1275372000,
'fecha_ultima_falla' => 0,
'total_fallas' => 0,
)
);
here is the function:
function changekeyname($array, $newkey, $oldkey)
{
foreach ($array as $key => $value)
{
if (is_array($value))
$array[$key] = changekeyname($value,$newkey,$oldkey);
else
{
$array[$newkey] = $array[$oldkey];
}
}
unset($array[$oldkey]);
return $array;
}
I like KernelM's solution, but I needed something that would handle potential key conflicts (where a new key may match an existing key). Here is what I came up with:
function swapKeys( &$arr, $origKey, $newKey, &$pendingKeys ) {
if( !isset( $arr[$newKey] ) ) {
$arr[$newKey] = $arr[$origKey];
unset( $arr[$origKey] );
if( isset( $pendingKeys[$origKey] ) ) {
// recursion to handle conflicting keys with conflicting keys
swapKeys( $arr, $pendingKeys[$origKey], $origKey, $pendingKeys );
unset( $pendingKeys[$origKey] );
}
} elseif( $newKey != $origKey ) {
$pendingKeys[$newKey] = $origKey;
}
}
You can then cycle through an array like this:
$myArray = array( '1970-01-01 00:00:01', '1970-01-01 00:01:00' );
$pendingKeys = array();
foreach( $myArray as $key => $myArrayValue ) {
// NOTE: strtotime( '1970-01-01 00:00:01' ) = 1 (a conflicting key)
$timestamp = strtotime( $myArrayValue );
swapKeys( $myArray, $key, $timestamp, $pendingKeys );
}
// RESULT: $myArray == array( 1=>'1970-01-01 00:00:01', 60=>'1970-01-01 00:01:00' )
Here is a helper function to achieve that:
/**
* Helper function to rename array keys.
*/
function _rename_arr_key($oldkey, $newkey, array &$arr) {
if (array_key_exists($oldkey, $arr)) {
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
return TRUE;
} else {
return FALSE;
}
}
pretty based on #KernelM answer.
Usage:
_rename_arr_key('oldkey', 'newkey', $my_array);
It will return true on successful rename, otherwise false.
this code will help to change the oldkey to new one
$i = 0;
$keys_array=array("0"=>"one","1"=>"two");
$keys = array_keys($keys_array);
for($i=0;$i<count($keys);$i++) {
$keys_array[$keys_array[$i]]=$keys_array[$i];
unset($keys_array[$i]);
}
print_r($keys_array);
display like
$keys_array=array("one"=>"one","two"=>"two");
Easy stuff:
this function will accept the target $hash and $replacements is also a hash containing newkey=>oldkey associations.
This function will preserve original order, but could be problematic for very large (like above 10k records) arrays regarding performance & memory.
function keyRename(array $hash, array $replacements) {
$new=array();
foreach($hash as $k=>$v)
{
if($ok=array_search($k,$replacements))
$k=$ok;
$new[$k]=$v;
}
return $new;
}
this alternative function would do the same, with far better performance & memory usage, at the cost of losing original order (which should not be a problem since it is hashtable!)
function keyRename(array $hash, array $replacements) {
foreach($hash as $k=>$v)
if($ok=array_search($k,$replacements))
{
$hash[$ok]=$v;
unset($hash[$k]);
}
return $hash;
}
This page has been peppered with a wide interpretation of what is required because there is no minimal, verifiable example in the question body. Some answers are merely trying to solve the "title" without bothering to understand the question requirements.
The key is actually an ID number and the value is a count. This is
fine for most instances, however I want a function that gets the
human-readable name of the array and uses that for the key, without
changing the value.
PHP keys cannot be changed but they can be replaced -- this is why so many answers are advising the use of array_search() (a relatively poor performer) and unset().
Ultimately, you want to create a new array with names as keys relating to the original count. This is most efficiently done via a lookup array because searching for keys will always outperform searching for values.
Code: (Demo)
$idCounts = [
3 => 15,
7 => 12,
8 => 10,
9 => 4
];
$idNames = [
1 => 'Steve',
2 => 'Georgia',
3 => 'Elon',
4 => 'Fiona',
5 => 'Tim',
6 => 'Petra',
7 => 'Quentin',
8 => 'Raymond',
9 => 'Barb'
];
$result = [];
foreach ($idCounts as $id => $count) {
if (isset($idNames[$id])) {
$result[$idNames[$id]] = $count;
}
}
var_export($result);
Output:
array (
'Elon' => 15,
'Quentin' => 12,
'Raymond' => 10,
'Barb' => 4,
)
This technique maintains the original array order (in case the sorting matters), doesn't do any unnecessary iterating, and will be very swift because of isset().
If you want to replace several keys at once (preserving order):
/**
* Rename keys of an array
* #param array $array (asoc)
* #param array $replacement_keys (indexed)
* #return array
*/
function rename_keys($array, $replacement_keys) {
return array_combine($replacement_keys, array_values($array));
}
Usage:
$myarr = array("a" => 22, "b" => 144, "c" => 43);
$newkeys = array("x","y","z");
print_r(rename_keys($myarr, $newkeys));
//must return: array("x" => 22, "y" => 144, "z" => 43);
You can use this function based on array_walk:
function mapToIDs($array, $id_field_name = 'id')
{
$result = [];
array_walk($array,
function(&$value, $key) use (&$result, $id_field_name)
{
$result[$value[$id_field_name]] = $value;
}
);
return $result;
}
$arr = [0 => ['id' => 'one', 'fruit' => 'apple'], 1 => ['id' => 'two', 'fruit' => 'banana']];
print_r($arr);
print_r(mapToIDs($arr));
It gives:
Array(
[0] => Array(
[id] => one
[fruit] => apple
)
[1] => Array(
[id] => two
[fruit] => banana
)
)
Array(
[one] => Array(
[id] => one
[fruit] => apple
)
[two] => Array(
[id] => two
[fruit] => banana
)
)
This basic function handles swapping array keys and keeping the array in the original order...
public function keySwap(array $resource, array $keys)
{
$newResource = [];
foreach($resource as $k => $r){
if(array_key_exists($k,$keys)){
$newResource[$keys[$k]] = $r;
}else{
$newResource[$k] = $r;
}
}
return $newResource;
}
You could then loop through and swap all 'a' keys with 'z' for example...
$inputs = [
0 => ['a'=>'1','b'=>'2'],
1 => ['a'=>'3','b'=>'4']
]
$keySwap = ['a'=>'z'];
foreach($inputs as $k=>$i){
$inputs[$k] = $this->keySwap($i,$keySwap);
}
This function will rename an array key, keeping its position, by combining with index searching.
function renameArrKey($arr, $oldKey, $newKey){
if(!isset($arr[$oldKey])) return $arr; // Failsafe
$keys = array_keys($arr);
$keys[array_search($oldKey, $keys)] = $newKey;
$newArr = array_combine($keys, $arr);
return $newArr;
}
Usage:
$arr = renameArrKey($arr, 'old_key', 'new_key');
this works for renaming the first key:
$a = ['catine' => 'cat', 'canine' => 'dog'];
$tmpa['feline'] = $a['catine'];
unset($a['catine']);
$a = $tmpa + $a;
then, print_r($a) renders a repaired in-order array:
Array
(
[feline] => cat
[canine] => dog
)
this works for renaming an arbitrary key:
$a = ['canine' => 'dog', 'catine' => 'cat', 'porcine' => 'pig']
$af = array_flip($a)
$af['cat'] = 'feline';
$a = array_flip($af)
print_r($a)
Array
(
[canine] => dog
[feline] => cat
[porcine] => pig
)
a generalized function:
function renameKey($oldkey, $newkey, $array) {
$val = $array[$oldkey];
$tmp_A = array_flip($array);
$tmp_A[$val] = $newkey;
return array_flip($tmp_A);
}
There is an alternative way to change the key of an array element when working with a full array - without changing the order of the array.
It's simply to copy the array into a new array.
For instance, I was working with a mixed, multi-dimensional array that contained indexed and associative keys - and I wanted to replace the integer keys with their values, without breaking the order.
I did so by switching key/value for all numeric array entries - here: ['0'=>'foo']. Note that the order is intact.
<?php
$arr = [
'foo',
'bar'=>'alfa',
'baz'=>['a'=>'hello', 'b'=>'world'],
];
foreach($arr as $k=>$v) {
$kk = is_numeric($k) ? $v : $k;
$vv = is_numeric($k) ? null : $v;
$arr2[$kk] = $vv;
}
print_r($arr2);
Output:
Array (
[foo] =>
[bar] => alfa
[baz] => Array (
[a] => hello
[b] => world
)
)
best way is using reference, and not using unset (which make another step to clean memory)
$tab = ['two' => [] ];
solution:
$tab['newname'] = & $tab['two'];
you have one original and one reference with new name.
or if you don't want have two names in one value is good make another tab and foreach on reference
foreach($tab as $key=> & $value) {
if($key=='two') {
$newtab["newname"] = & $tab[$key];
} else {
$newtab[$key] = & $tab[$key];
}
}
Iterration is better on keys than clone all array, and cleaning old array if you have long data like 100 rows +++ etc..
One which preservers ordering that's simple to understand:
function rename_array_key(array $array, $old_key, $new_key) {
if (!array_key_exists($old_key, $array)) {
return $array;
}
$new_array = [];
foreach ($array as $key => $value) {
$new_key = $old_key === $key
? $new_key
: $key;
$new_array[$new_key] = $value;
}
return $new_array;
}
Here is an experiment (test)
Initial array (keys like 0,1,2)
$some_array[] = '6110';//
$some_array[] = '6111';//
$some_array[] = '6210';//
I must change key names to for example human_readable15, human_readable16, human_readable17
Something similar as already posted. During each loop i set necessary key name and remove corresponding key from the initial array.
For example, i inserted into mysql $some_array got lastInsertId and i need to send key-value pair back to jquery.
$first_id_of_inserted = 7;//lastInsertId
$last_loop_for_some_array = count($some_array);
for ($current_loop = 0; $current_loop < $last_loop_for_some_array ; $current_loop++) {
$some_array['human_readable'.($first_id_of_inserted + $current_loop)] = $some_array[$current_loop];//add new key for intial array
unset( $some_array[$current_loop] );//remove already renamed key from array
}
And here is the new array with renamed keys
echo '<pre>', print_r($some_array, true), '</pre>$some_array in '. basename(__FILE__, '.php'). '.php <br/>';
If instead of human_readable15, human_readable16, human_readable17 need something other. Then could create something like this
$arr_with_key_names[] = 'human_readable';
$arr_with_key_names[] = 'something_another';
$arr_with_key_names[] = 'and_something_else';
for ($current_loop = 0; $current_loop < $last_loop_for_some_array ; $current_loop++) {
$some_array[$arr_with_key_names[$current_loop]] = $some_array[$current_loop];//add new key for intial array
unset( $some_array[$current_loop] );//remove already renamed key from array
}
Hmm, I'm not test before, but I think this code working
function replace_array_key($data) {
$mapping = [
'old_key_1' => 'new_key_1',
'old_key_2' => 'new_key_2',
];
$data = json_encode($data);
foreach ($mapping as $needed => $replace) {
$data = str_replace('"'.$needed.'":', '"'.$replace.'":', $data);
}
return json_decode($data, true);
}
You can write simple function that applies the callback to the keys of the given array. Similar to array_map
<?php
function array_map_keys(callable $callback, array $array) {
return array_merge([], ...array_map(
function ($key, $value) use ($callback) { return [$callback($key) => $value]; },
array_keys($array),
$array
));
}
$array = ['a' => 1, 'b' => 'test', 'c' => ['x' => 1, 'y' => 2]];
$newArray = array_map_keys(function($key) { return 'new' . ucfirst($key); }, $array);
echo json_encode($array); // {"a":1,"b":"test","c":{"x":1,"y":2}}
echo json_encode($newArray); // {"newA":1,"newB":"test","newC":{"x":1,"y":2}}
Here is a gist https://gist.github.com/vardius/650367e15abfb58bcd72ca47eff096ca#file-array_map_keys-php.

Categories