This question already has answers here:
Filter/Remove rows where column value is found more than once in a multidimensional array
(4 answers)
Closed 3 months ago.
Array
(
[0] => Array
(
[file] => /var/websites/example.com/assets/images/200px/1419050406e6648e1c766551a0ffc91380fd6ff3406002011-10-233750.jpg
[md5] => 42479bee7a304d2318250de2ef1962a9
[url] => http://example.com/assets/images/200px/1419050406e6648e1c766551a0ffc91380fd6ff3406002011-10-233750.jpg
)
[1] => Array
(
[file] => /var/websites/example.com/assets/images/200px/21277792606e6648e1c766551a0ffc91380fd6ff3406002011-10-233750.jpg
[md5] => 42479bee7a304d2318250de2ef1962a9
[url] => http://example.com/assets/images/200px/21277792606e6648e1c766551a0ffc91380fd6ff3406002011-10-233750.jpg
)
)
How can I remove md5 key duplicates from above array?
<?php
$data = array(
array(
'md5' => 'alpha',
'some' => 'value',
),
array(
'md5' => 'alpha',
'some' => 'other value',
),
array(
'md5' => 'bravo',
'some' => 'third value',
),
);
// walk input array
$_data = array();
foreach ($data as $v) {
if (isset($_data[$v['md5']])) {
// found duplicate
continue;
}
// remember unique item
$_data[$v['md5']] = $v;
}
// if you need a zero-based array, otheriwse work with $_data
$data = array_values($_data);
PHP does already have a function to remove duplicate elements from an array. But unfortunately, array_unique does only support string based comparisons:
Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same. The first element will be used.
The problem is that any array turned into a string is equal to any other array:
Arrays are always converted to the string "Array"; […]
But you can use the uniqueness of array keys to solve this:
$index = array();
foreach ($arr as $key => $item) {
if (isset($index[$item['md5']])) {
unset($item[$key]);
}
$index[$item['md5']] = TRUE;
}
Here is my final function after your help guys.... Hope it helps somebody in the future...
$data = array(
array(
'md5' => 'alpha',
'some' => 'value',
),
array(
'md5' => 'alpha',
'some' => 'other value',
),
array(
'md5' => 'bravo',
'some' => 'third value',
),
);
// walk input array
function remove_duplicateKeys($key,$data){
$_data = array();
foreach ($data as $v) {
if (isset($_data[$v[$key]])) {
// found duplicate
continue;
}
// remember unique item
$_data[$v[$key]] = $v;
}
// if you need a zero-based array
// otherwise work with $_data
$data = array_values($_data);
return $data;
}
$my_array = remove_duplicateKeys("md5",$data);
As array_unique operates on flat arrays, you can not use it directly. But you can first map all 'md5' values to a flat array, unique it and then get the elements with array_intersect_key:
$allMd5s = array_map(function($v) {return $v['md5'];}, $array);
$uniqueMd5s = array_unique($md5);
$result = array_intersect_key($arr, $uniqueMd5s);
Use array_filter(). Quick code test (doesn't necessarily mirror your situation, but you should get the idea):
<?php
header('Content-Type: Text/Plain');
$array = array(
0 => array('name' => 'samson'),
1 => array('name' => 'delilah'),
2 => array('name' => 'samson'),
3 => array('name' => 'goliath'),
);
$md5Processed = array();
$newArray = array_filter($array, "uniqueMD5");
print_r($newArray);
exit;
function uniqueMD5( $data ){
global $md5Processed;
if( !in_array( $data['name'], $md5Processed ) )
{
$md5Processed[] = $data['name'];
return true;
}
}
// your array
$array = array(...);
// will be used to store md5 hashes
$md5 = array();
// walk through array
foreach ($array as $key => $arr) {
// have we already seen this md5 hash?
if (in_array($arr['md5'], $md5)){
// remove element
unset($array[$key]);
}else {
// keep element, but add it's md5
$md5[] = $arr['md5'];
}
}
Tldr.: The obligatory one-liner:
$out = array_values ( array_intersect_key( $in, array_unique( array_column( $in, 'md5' ) ) ) );
And step by step (with my own example):
$in = [
[ 'k' => 'a' ],
[ 'k' => 'a' ],
[ 'k' => 'b' ]
];
print_r( array_column( $in, 'k' ) );
array_column returns a flat array, only with the values from all 'k'-keys:
Array
(
[0] => a
[1] => a
[2] => b
)
We can now filter that with array_unique:
print_r( array_unique( array_column( $in, 'k' ) ) ) );
To get:
Array
(
[0] => a
[2] => b
)
See that we kept the keys of the original array (0 and 2)?
We can use that with array_intersect_key. It computes the intersection of arrays using keys for comparison.
print_r( array_intersect_key( $in, array_unique( array_column( $in, 'k' ) ) ) );
We get the first [0] and third [2] entry of our original array.
Array
(
[0] => Array
(
[k] => a
)
[2] => Array
(
[k] => b
)
)
Now the keys are messed up (which might lead to problems if for loops), so we wrap the whole thing in array_values:
print_r(
array_values( array_intersect_key( $in, array_unique( array_column( $in, 'k' ) ) ) )
);
Array
(
[0] => Array
(
[k] => a
)
[1] => Array
(
[k] => b
)
)
Take a look at the function array_unique.
Related
I want to come up with $desired_output from given $data array below using array_map() or any PHP array_* built-in function. Any idea how?
$data = array(
'posted_name' => 'John Doe',
'posted_age' => '30',
);
$desired_output = array(
'name' => 'John Doe',
'age' => '30',
);
I tried below but the result is an array of arrays:
$desired_output = array_map(
function($keys, $vals)
{
$new[substr($keys, strlen('posted_'))] = $vals;
return $new;
},
array_keys($data),
$data
);
print_r($desired_output );
/**
* Array (
[0] => Array ( [name] => John Doe )
[1] => Array ( [age] => 30 )
)
*/
What about the following:
$desired_output = array();
array_walk(
$data,
function(&$val, $key) use (&$desired_output)
{
$len = strlen('posted_');
if (substr($key, 0, $len) == "posted_") {
$key = substr($key, $len);
}
$desired_output[$key] = $val;
}
);
print_r($desired_output);
Changes:
Using array_walk instead of array_map since _map cannot handle keys
Creating an empty array to build the result in
Using that array in the scope of the lambda function
Replacing the "posted_" only if it exists in the key
Output:
$ php -f ~/tmp/so2.php
Array
(
[name] => John Doe
[age] => 30
)
I want to remove null values from this array.
Array(
[0] => Array( [fcmToken] => 123 )
[1] => Array( [fcmToken] => )
[2] => Array( [fcmToken] => 789 )
)
Expected Results
Array(
[0] => Array( [fcmToken] => 123 )
[1] => Array( [fcmToken] => 789 )
)
Here we are using foreach to iterate over values and using $value for non-empty $value["fcmToken"]
$result=array();
foreach($array as $key => $value)
{
if(!empty($value["fcmToken"]))
{
$result[]=$value;
}
}
print_r($result);
Output:
Array
(
[0] => Array
(
[fcmToken] => dfqVhqdqhpk
)
[1] => Array
(
[fcmToken] => dfgdfhqdqhpk
)
)
Use array_filter with a callback:
$r = array_filter($array, function($v) { return !empty($v['fcmToken']); });
For checking exactly null:
$r = array_filter($array, function($v) { return !is_null($v['fcmToken']); });
The best and easy single line solution for filter multidimensional array like below.
$aryMain = array_filter(array_map('array_filter', $aryMain));
I hope this solution is work for you. for more detail please visit below link.
PHP: remove empty array strings in multidimensional array
Danger! Do not trust empty() when dealing with numbers (or number-ish) values that could be zero!!! The same is true with using array_filter without a specific filtering parameter (as several answers on this page are using).
Look at how you can get the wrong output:
Bad Method:
$array = array(
array("fcmToken" => '0'),
array("fcmToken" => 123),
array("fcmToken" => ''),
array("fcmToken" => 789),
array("fcmToken" => 0)
);
$result=array(); // note, this line is unnecessary
foreach($array as $key => $value){ // note, $key is unnecessary
if(!empty($value["fcmToken"])){ // this is not a reliable method
$result[]=$value;
}
}
var_export($result);
Output:
array (
0 =>
array (
'fcmToken' => 123,
),
1 =>
array (
'fcmToken' => 789,
),
)
The zeros got swallowed up!
This is how it should be done:
Instead, you should use strlen() to check the values:
Method #1: foreach()
foreach($array as $sub){
if(strlen($sub["fcmToken"])){
$result[]=$sub;
}
}
var_export($result);
Method #2: array_filter() w/ anonymous function and array_values() for consistency
var_export(array_values(array_filter($array,function($a){return strlen($a['fcmToken']);})));
Output for either method:
array (
0 =>
array (
'fcmToken' => '0',
),
1 =>
array (
'fcmToken' => 123,
),
2 =>
array (
'fcmToken' => 789,
),
3 =>
array (
'fcmToken' => 0,
),
)
Demonstration of bad method and two good methods.
You need to do it using array_map, array_filter and array_values. Check below code :
$entry = array(
array("fcmToken" => 'dfqVhqdqhpk'),
array("fcmToken" => ''),
array("fcmToken" => 'dfgdfhqdqhpk'),
);
echo "<pre>";
$entry = array_values(array_filter(array_map('array_filter', $entry)));
print_r($entry);
Use this recursive function which iterates over any subarray and remove all elements with a null-value.
function removeNullValues ($array) {
foreach ($array as $key => $entry) {
if (is_array($entry)) {
$array[$key] = removeNullValues($entry);
if ($array[$key] === []) {
unset($array[$key]);
}
} else {
if ($entry === null) {
unset($array[$key]);
}
}
}
return $array;
}
Try using array_map() to apply the filter to every array in the array.
I am trying to uniformly rename all of my array keys using a foreach loop and unset.
Array before:
Array
( [0] => Array (
[store_nl] => Store One
[id_nl] => 123456
[title_nl] => Product One
[price_nl] => $9.00 )
[1] => Array (
[store_ds] => Store Two
[id_ds] => 789012
[title_ds] => Product Two
[price_ds] => $8.00 )
)
foreach using unset:
if(isset($data)){
foreach ( $data as $k=>$v )
{
//Store One (ds)
$data[$k]['Store'] = $data[$k]['store_ds'];
$data[$k]['ItemID'] = $data[$k]['id_ds'];
$data[$k]['Description'] = $data[$k]['title_ds'];
$data[$k]['Price'] = $data[$k]['price_ds'];
unset($data[$k]['store_ds']);
unset($data[$k]['id_ds']);
unset($data[$k]['title_ds']);
unset($data[$k]['price_ds']);
//Store Two (nl)
$data[$k]['Store'] = $data[$k]['store_nl'];
$data[$k]['ItemID'] = $data[$k]['id_nl'];
$data[$k]['Description'] = $data[$k]['title_nl'];
$data[$k]['Price'] = $data[$k]['price_nl'];
unset($data[$k]['store_nl']);
unset($data[$k]['id_nl']);
unset($data[$k]['title_nl']);
unset($data[$k]['price_nl']);
}
}
Array after:
Array
( [0] => Array (
[Store] => Store One
[ItemID] => 123456
[Description] => Product One
[Price] => $9.00 )
[1] => Array (
[Store] =>
[ItemID] =>
[Description] =>
[Price] => )
)
All of the array keys have been changed, but some of the data is now gone? Can someone please tell me a better way to do this without data loss?
The following will do what you deed:
$myArray = array(
array(
'store_ni' => 'Store One',
'id_ni' => 123456,
'title_ni' => 'Product One',
'price_ni' => '$9.00'
),
array(
'store_ds' => 'Store Two',
'id_ds' => 789012,
'title_ds' => 'Product Two',
'price_ds' => '$8.00'
)
);
$newKeys = array('Store', 'ItemID', 'Description', 'Price');
$result = array();
foreach($myArray as $innerArray)
{
$result[] = array_combine(
$newKeys,
array_values($innerArray)
);
}
array_combine() combines the first array you pass to it and assign it as the keys of the returned array and the second array you pass it as the values of that array.
Use array_flip() to flip between the keys and values, then it'll be easy to run over the values and "flip" the array again (back to its original state). It should be something like:
$tmp_arr = array_flip($arr);
$fixed_arr = array();
foreach($tmp_arr as $k => $v){
if($val == "store_nl"){
$fixed_arr[$k] = "Store";
}
// etc...
}
// and now flip back:
$arr = array_flip($fixed_arr);
The arrays should have the same number of elements, but I think that's the case here.
$data = [[
'store_nl' => 'Store One',
'id_nl' => 123456,
'title_nl' => 'Product One',
'price_nl' => '$9.00'
], [
'store_ds' => 'Store Two',
'id_ds' => 789012,
'title_ds' => 'Product Two',
'price_ds' => '$8.00'
]];
$keys = ['Store', 'ItemID', 'Description', 'Price'];
$data = [
'nl' => array_combine($keys, $data[0]),
'ds' => array_combine($keys, $data[1])
];
Recursive php rename keys function:
function replaceKeys($oldKey, $newKey, array $input){
$return = array();
foreach ($input as $key => $value) {
if ($key===$oldKey)
$key = $newKey;
if (is_array($value))
$value = replaceKeys( $oldKey, $newKey, $value);
$return[$key] = $value;
}
return $return;
}
This is one array
Array ( [0] => 1 [1] => 2 )
The associative array is
Array ( [0] => Array ( [id_1] => 3 [id_2] => 1 ) [1] => Array ( [id_3] => 5 [id_4] => 3 ) )
I want to compare these arrays and get an array containing the values that are not present in the associative array
The result should be array(3) and array(5,3)
$array1 = array(1,2);
$array2 = array(
array(
'id_1' => 3,
'id_2' => 1
),
array(
'id_3' => 5,
'id_4' => 3,
)
);
I'm not sure if I understand the question, if you want to compare each array individually:
foreach($array2 as $subarray) {
var_dump(array_diff($subarray, $array1));
}
returns:
array
'id_1' => int 3
array
'id_3' => int 5
'id_4' => int 3
Otherwise if you don't want to loop through and flatten the array (using an array_flatten function from the php doc comments of array_values)
http://www.php.net/manual/en/function.array-values.php#97715
function array_flatten($a,$f=array()){
if(!$a||!is_array($a))return '';
foreach($a as $k=>$v){
if(is_array($v))$f=array_flatten($v,$f);
else $f[$k]=$v;
}
return $f;
}
var_dump(
array_unique(
array_diff(
array_flatten($array2), $array1
)
)
);
returns:
array
'id_1' => int 3
'id_3' => int 5
'id_4' is not shown because it doesn't have a unique value. You can remove the keys with array_values() or leave in 'id_4' by removing the array_unique() from the code.
something like this, maybe?
$array_a = array(1, 2);
$array_b = array(array(1, 3), array(3, 1));
$result = array();
foreach($array_b as $key => $sub_array_b){
foreach($sub_array_b as $sub_key => $value){
if(!in_array($value, $array_a) && !in_array($value, $result)) array_push($result, $value);
}
}
EDIT: I typed this before you edited your question. You can still modify the code above so that it creates a different array for each subarray in your associative array.
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);
}