I have the following two arrays:
$array_one = array('colorZero'=>'black', 'colorOne'=>'red', 'colorTwo'=>'green', 'colorThree'=>'blue', 'colorFour'=>'purple', 'colorFive'=>'golden');
$array_two = array('colorOne', 'colorTwo', 'colorThree');
I want an array from $array_one which only contains the key-value pairs whose keys are members of $array_two (either by making a new array or removing the rest of the elements from $array_one)
How can I do that?
I looked into array_diff and array_intersect, but they compare values with values, and not the values of one array with the keys of the other.
As of PHP 5.1 there is array_intersect_key (manual).
Just flip the second array from key=>value to value=>key with array_flip() and then compare keys.
So to compare OP's arrays, this would do:
$result = array_intersect_key( $array_one , array_flip( $array_two ) );
No need for any looping the arrays at all.
Update
Check out the answer from Michel: https://stackoverflow.com/a/30841097/2879722. It's a better and easier solution.
Original Answer
If I am understanding this correctly:
Returning a new array:
$array_new = [];
foreach($array_two as $key)
{
if(array_key_exists($key, $array_one))
{
$array_new[$key] = $array_one[$key];
}
}
Stripping from $array_one:
foreach($array_one as $key => $val)
{
if(array_search($key, $array_two) === false)
{
unset($array_one[$key]);
}
}
Tell me if it works:
for($i=0;$i<count($array_two);$i++){
if($array_two[$i]==key($array_one)){
$array_final[$array_two[$i]]=$array_one[$array_two[$i]];
next($array_one);
}
}
This question already has answers here:
How to filter an associative array comparing keys with values in an indexed array?
(12 answers)
Closed 4 months ago.
Here is data
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
I need to get in result:
$result = array(
'random' => 1,
'xoxo' => 3,
'temp' => 5,
);
I mean keys presence/order from $fields apply to $array.
The question is:
Can I perform this transformation using only array_ functions? (I don't wanna use iteations)
If yes: can you link me function that I need?
(sorry for spelling mistakes)
upd.
PHP 5.2
$result=array_intersect_key($array ,array_flip($fields) );
// little trick required here...
$fields = array('random' => 0, 'xoxo' => 0, 'temp' => 0);
$result = array_intersect_key($array,$fields);
I am always interested in these types of question, where it's about efficient code (both in code-usage and speed). That said, I tried and benchmarked several different methods, and nothing was as efficient and as a simple foreach!
I tried all the solutions posted, and my own array_walk and basic foreach. I ran several test, both with the arrays and fields posted by Miraage, and some with much larger arrays. I also noted anything odd with the results, such as additional values if $fields had values not in $array.
I've ordered it by speed.
FOREACH: 0.01245 sec
$result = array();
foreach ($fields as $k)
{
if (isset($array[$k]))
$result[$k] = $array[$k];
}
ARRAY_DIFF_KEY: 0.01471 sec (unexpected results: additional values)
$result = array_diff_key($fields, $array);
FOREACH (function): 0.02449 sec
function array_filter_by_key($array, $fields)
{
$result = array();
foreach ($fields as $k)
{
if (isset($array[$k]))
$result[$k] = $array[$k];
}
return $result;
}
ARRAY_WALK (by reference): 0.09123 sec
function array_walk_filter_by_key($item, $key, $vars)
{
if (isset($vars[1][$item]))
$vars[0][$item] = $vars[1][$item];
}
$result = array();
array_walk($fields, 'array_walk_filter_by_key', array(&$result, &$array));
LIST/EACH: 0.12456 sec
$result = array();
reset($fields);
while (list($key, $value) = each($fields))
{
if (isset($array[$value]))
$result[$value] = $array[$value];
}
ARRAY_INTERSECT_KEY: 0.27264 sec (incorrect order)
$result = array_intersect_key($array, array_flip($fields));
ARRAY_REPLACE (array_intersect_key second): 0.29409 sec (unexpected results: additional values)
$result = array_replace(
array_fill_keys($fields, false),
array_intersect_key($array, array_flip($fields))
);
ARRAY_REPLACE (two array_intersect_key): 0.33311 sec
$flip = array_flip($fields);
$result = array_replace(
array_intersect_key($flip, $array),
array_intersect_key($array, $flip)
);
ARRAY_WALK (set null): 3.35929 sec (unexpected results: additional values)
function array_walk_filter_by_key_null(&$item, $key, $array)
{
if (isset($array[$key]))
$item = $array[$key];
else
$item = null;
}
$result = array_flip($fields);
array_walk($result, 'array_walk_filter_by_key_null', $array);
ARRAY_REPLACE (array_intersect_key first): 11.11044 sec
$flip = array_flip($fields);
$result = array_intersect_key(
array_replace($flip, $array),
array_intersect_key($flip, $array)
);
ARRAY_MERGE: 14.11296 sec (unexpected results: additional values)
$result = array_splice(
array_merge(array_flip($fields), $array),
0,
count($fields)
);
So there it is. Can't beat a DIY. Sometimes the perception is that the built-in functions are faster, but it's not always the case. Compilers are pretty good these days.
This code preserves order and works in PHP 5.2 as required
One line:
$result = array_merge( array_flip($fields),
array_intersect_key(
$array,
array_flip( $fields )
)
);
For performance:
$flip = array_flip($fields);
$result = array_merge( $flip
array_intersect_key(
$array,
$flip
)
);
I believe this works as you require.
$result = array_splice(array_merge(array_flip($fields) , $array) , 0 , count($fields));
Just to solve the puzzle:
$result = array_replace(
array_intersect_key(array_flip($fields), $array),
array_intersect_key($array, array_flip($fields))
);
The first array_intersect creates the list of fields in good order, the other one overcomes array_replace functionality to create the the keys that do not exist in the first array.
Meets your requirements. But I wouldn't use it in any production code, as this may be pretty heavy (I didn't benchmark though, so it's just a gut feeling). An array_walk solution seems lighter.
If you want to keep key order from $fields, you could try this: (if key not exists in $array, then the value for that key will be null.)
$result = array_flip($fields);
array_walk($result, function(&$item, $key, $array) {
$item = isset($array[$key]) ? $array[$key] : null;
}, $array);
var_dump($result);
I will consider that you can't change the input (neither $array or $fields).
This can be achieved if you have an array that uses as keys the values from $fields. After that you can merge the two (with $fields being the first parameter) and remove the extra elements.
Considering that you can't change $fields, I will create it:
$tmp = array_combine($fields, range(1, count($fields)));
$result = array_merge($tmp, $array);
$result = array_splice($result, 0, count($fields));
The full working sample (with some comments) can be found here: http://codepad.org/H0CDN7ok
My attempt:
array_replace(
array_fill_keys($fields, false),
array_intersect_key($array, # Keys in array, without order
array_flip($fields))));
It was easy to get the keys in the same order as $array. Then to get them in the proper order, I built an array with keys equal to $fields. Array_replace did the rest.
The solution is "stable" in that missing keys in $array will be replaced with FALSE and can thus be filtered out if need be.
array_flip walks the field array of size N once, array_intersect walks M time a N sized array, array_fill_keys costs N and the final array_replace is, I believe, N^2.
So total cost is M*N^5.
Walking the smallest array and picking the values from the large one is O(M^2*N^2), so for large values of N I suspect that the PHP solution might prove faster. This doesn't enter keys which are not in the data array.
$answer = array();
foreach($fields as $fld) // N-sized cycle
if (isset($array[$fld])) // Cost M
$answer[$fld] = // Assignment is N*1/2
$array[$fld]; // Getting value is another M
(some time and much puzzlement later)
I ran a check and I think I must be making some silly mistake, for the times I'm getting are totally nonsensical. Admittedly I'm using a very short $fields array, so I'd expect skewed results, but not this skewed. Unless $answer[$fld] is calculated with some REALLY clever hash trick, whereby the true cost of the interpreted solution is not O(M^2*N^2) but O(K*N^2) with K small.
If anyone wants to play with times or tell me what stupid mistake I might have made, here's the benchmark.
I was of two minds about posting this, because the other obvious explanation is that I made a ridiculous, silly mistake somewhere and I'm going to end up with egg on my face, but, oh, what the hell.
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
// Let's not print anything just yet, maybe that's what screwing the timer?
$results = '';
$duh = 0;
for ($cycle = 0; $cycle < 10; $cycle++)
{
// Add some more elements to $array.
for ($i = 0; $i < 10000; $i++)
{
$k = uniqid();
$array[$k] = 42;
}
$start = explode(' ', microtime());
// WTF? Do more cycles to average the timing.
for ($j = 0; $j < 10; $j++)
{
// 0 or 1 to switch
if (1)
{
// INTERPRETED ANSWER
$answer = array();
foreach($fields as $fld) // N-sized cycle
if (isset($array[$fld])) // Cost M
$answer[$fld] = // Assignment is N*1/2
$array[$fld]; // Getting value is another M
} else {
// FUNCTION ANSWER
$answer = array_replace(
array_fill_keys($fields, false),
// array_combine($fields, $fields),
array_intersect_key($array, # Keys in array, without order
array_flip($fields)));
}
// USE $answer so to avoid premature optimization?
// You can't be that clever.
$duh += strlen(serialize($answer));
}
$stop = explode(' ', microtime());
// An error in timing? Check via a stupid roundabout.
$int = $stop[1]-$start[1]+1;
$int += ($stop[0]-$start[0]);
$int -= 1;
$elapsed = number_format($int * 1000000, 2);
$results .= "".(5000*$cycle)." = $elapsed us.\n";
}
// I need to get in result:
$wanted = array(
'random' => 1,
'xoxo' => 3,
'temp' => 5,
);
// DID we get the right answer?
print "Wanted:\n"; print_r($wanted);
print "Gotten:\n"; print_r($answer);
print "Results: $results\n$duh -- count of array is " . count($array);
// And yet I have always the same realtime, name of a dog, how can that be?
// I must be doing something REALLY REALLY wrong somewhere.
Easy Way:
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4,
);
$fields = array('random', 'xoxo', 'temp');
$output = array();
foreach ($fields as $value)
if(isset($array[$value]))
$output[$value]=$array[$value];
This is a solution that also handles the case when some $fields are not present as keys in $array:
$flip = array_flip($fields);
$result = array_intersect_key(array_replace($flip, $array), array_intersect_key($flip, $array));
If all $fields are known to be present as keys in $array there is this simpler solution:
$flip = array_flip($fields);
$result = array_intersect_key(array_replace($flip, $array), $flip);
which can be written as a one-liner like this:
$result = array_intersect_key(array_replace($flip=array_flip($fields), $array), $flip);
If some $fields are not keys of $array, but $array contains counts, so that it makes sense to return a 0 count for missing keys, we can replace flip() with array_fill_keys($fields, 0):
$result = array_intersect_key(array_replace($fill=array_fill_keys($fields, 0), $array), $fill);
to which we can apply an array_filter() to filter out the 0s again, if needed. By replacing 0 with false or null we can flag and handle the absence of a key in $array when the values are not counts.
The sad thing is that these solutions, like all others on this page, have to work through all keys of the $array, while any explicit loop would be on $fields. For the time being, it seems that when count($array) is much bigger than count($fields) there is no array-function-based solution as fast as an explicit loop (since they would explicitly construct the result in callback functions, I consider array_walk() and array_reduce() to be explicit loops here).
The problem is that none of the available array_ functions breaks the association between keys and values, and since we would like to loop on $fields, or rather a flipped array thereof, also to conserve its sort order, while keeping the values of $array, we are out of luck.
try:
$result=array();
reset($fields);
while(list($key,$value)=each($fields))
{
if(isset($array[$value])) $result[$value]=$array[$value];
}
This will work and preserve order for you:
$fields = array_flip($fields);
array_merge($fields,array_intersect_key($array, $fields));
$fields = array_keys($fields);
note, you could just call array_flip twice, but the above seemed a bit "cleaner".
The PHP function is called array_diff_key.
Sample code:
$array = array(
'random' => 1,
'pewpew' => 2,
'temp' => 5,
'xoxo' => 3,
'qweqweqe' => 4
);
$fields = array('random', 'xoxo', 'temp');
$result = array_diff_key($fields, $array);
This will produce the desired result.
Demo: http://shaquin.tk/experiments/array1.php
EDIT: If $fields can contain a value that is not a key in $array, then use this code:
$result = array_diff_key($fields, $array);
$result = array_intersect_key(array_flip($fields), $array);
$result = array_flip(array_diff(array_keys($result), $array));
$result = array_replace($result, $array);
$result = array_flip(array_intersect(array_flip($result), $fields));
It may be possible to optimize this a bit, but it works!
Note: I cannot link to an example, since my (hosted) site doesn't have >= PHP 5.3, however, I can link to a similar one: http://shaquin.tk/experiments/array2.php.
I know there is array_unique function, but I want to remove duplicates. Is there a built-in function or do I have to roll my own.
Example input:
banna, banna, mango, mango, apple
Expected output:
apple
You can use a combination of array_unique, array_diff_assoc and array_diff:
array_diff($arr, array_diff_assoc($arr, array_unique($arr)))
You can use
$singleOccurences = array_keys(
array_filter(
array_count_values(
array('banana', 'mango', 'banana', 'mango', 'apple' )
),
function($val) {
return $val === 1;
}
)
)
See
array_count_values — Counts all the values of an array
array_filter — Filters elements of an array using a callback function
array_keys — Return all the keys or a subset of the keys of an array
callbacks
Just write your own simple foreach loop:
$used = array();
$array = array("banna","banna","mango","mango","apple");
foreach($array as $arrayKey => $arrayValue){
if(isset($used[$arrayValue])){
unset($array[$used[$arrayValue]]);
unset($array[$arrayKey]);
}
$used[$arrayValue] = $arrayKey;
}
var_dump($array); // array(1) { [4]=> string(5) "apple" }
have fun :)
If you want to only leave values in the array that are already unique, rather than select one unique instance of each value, you will indeed have to roll your own. Built in functionality is just there to sanitise value sets, rather than filter.
You want to remove any entries that have duplicates, so that you're left with only the entries that were unique in the list?
Hmm it does sound like something you'll need to roll your own.
There is no existing function; You'll have to do this in two passes, one to count the unique values and one to extract the unique values:
$count = array();
foreach ($values as $value) {
if (array_key_exists($value, $count))
++$count[$value];
else
$count[$value] = 1;
}
$unique = array();
foreach ($count as $value => $count) {
if ($count == 1)
$unique[] = $value;
}
The answer on top looks great, but on a side note: if you ever want to eliminate duplicates but leave the first one, using array_flip twice would be a pretty simple way to do so. array_flip(array_flip(x))
Only partially relevant to this specific question - but I created this function from Gumbo's answer for multi dimensional arrays:
function get_default($array)
{
$default = array_column($array, 'default', 'id');
$array = array_diff($default, array_diff_assoc($default, array_unique($default)));
return key($array);
}
In this example, I had cached statuses and each one other than the default was 0 (the default was 1). I index the default array from the IDs, and then turn it into a string. So to be clear - the returned result of this is the ID of the default status providing it's in the same part of the multi dimensional array and not the key of it
PHP.net http://php.net/manual/en/function.array-unique.php
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
Takes an input array and returns a new array without duplicate values.
New solution:
function remove_dupes(array $array){
$ret_array = array();
foreach($array as $key => $val){
if(count(array_keys($val) > 1){
continue;
} else {
$ret_array[$key] = $val;
}
}
Each item in my array is an array of about 5 values.. Some of them are numerical ending in "GB".. I need the same array but with "GB" stripped out so that just the number remains.
So I need to iterate through my whole array, on each subarray take each value and strip the string "GB" from it and create a new array from the output.
Can anyone recommend and efficient method of doing this?
You can use array_walk_recursive() for this:
array_walk_recursive($arr, 'strip_text', 'GB');
function strip_text(&$value, $key, $string) {
$value = str_replace($string, '', $value);
}
It's a lot less awkward than traversing an array with its values by reference (correctly).
You can create your own custom function to iterate through an array's value's, check if the substring GB exists, and if so, remove it. With that function you can pass the original array and the function into array_map
// i only did the subarray part
$diskSizes = array("500", "300 GB", "200", "120 GB", "130GB");
$newArray = array();
foreach ($diskSizes as $diskSize) {
$newArray[] = str_replace('GB', '', $diskSize);
}
// look at the new array
print_r($newArray);
$arr = ...
foreach( $arr as $k => $inner ) {
foreach( $inner as $kk => &$vv ) {
$vv = str_replace( 'GB', '', $vv );
}
}
This actually keeps the original arrays intact with the new strings. (notice &$vv, which means that i'm getting the variable by reference, which means that any changes to $vv within the loop will affect the actual string, not by copying)