PHP - Get the key that has a value in multiple indexes - php

I have the below array:
$myArray = [
[
"name" => null,
"price" => [
"height" => 0.0098974902792506,
"left" => 0.8385,
"page" => 1,
"top" => 0.51290208554259,
"width" => 0.0275,
],
],
[
"name" => null
"price" => [
"height" => 0.0098974902792506,
"left" => 0.838,
"page" => 1,
"top" => 0.56981265464829,
"width" => 0.028,
]
],
[
"name" => null
"price" => [
"height" => 0.010250972074938,
"left" => 0.5905,
"page" => 1,
"top" => 0.44114528101803,
"width" => 0.0285,
]
]
];
I am trying to check the array and get the name of the key that has a value (is not null) in each array. In the above example, this would be price.
However, the array could also look like this:
[
[
"name" => null,
"price" => [
"height" => 0.0098974902792506,
"left" => 0.8385,
"page" => 1,
"top" => 0.51290208554259,
"width" => 0.0275,
],
],
[
"name" => null
"price" => null
],
[
"name" => null
"price" => null
]
]
In this case, there is not an array key that has a value in all of the arrays.
Below is my attempt to achieve this:
$originalKeyWithValue = null;
foreach($myArray as $key => $item)
{
$originalKeyWithValue = array_key_first($item);
if (isset($myArray[$key+1])) {
$nextKeyWithValue = array_key_first($myArray[$key+1]);
if($originalKeyWithValue != $nextKeyWithValue){
$originalKeyWithValue = $nextKeyWithValue;
}
}
}
return $originalKeyWithValue;
However the code above returns name as the key, even though it is null in all of the arrays in $myArray.

This is what would I do:
// I take first element of array as a source for indexes
foreach ($myArray[0] as $index => $item) {
// next I extract all elements from all subarrays under current `$index`
$values = array_column($myArray, $index);
// then I filter values to remove nulls.
// This also removes 0, empty arrays, false,
// so maybe you should change filter process
$values_filtered = array_filter($values);
// if number of filtered items is same as in original array - no nulls found
if (count($values_filtered) === count($values)) {
echo $index;
// optionally
// break;
}
}

Although there is an accepted answer, I thought I would share a way to do this using Laravel collections.
$uniqueKeysWithValues = collect($myArray)->map(function($item){
return array_keys( collect($item)->filter()->toArray() ); //filter will remove all null
})->flatten()->unique();
This approach will give you all keys that has values in it, even if there are values in both keys.

Related

Merge two multidimensional arrays using recursion, but not on full data set

I'm trying to add 2 array data to each other with array_merge(). It's just attached to the back. But lower levels are ignored.
Is there an alternative to array_merge() that will merge the user values without duolicating the color values?
Existing array data:
$existingtArr = [
"A" => [
"color" => 'red',
"user" => [
"Daniel" => ["01:18:08", "04:10:12"],
"Max" => ["01:04:00"],
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52"],
"Hugo" => ["07:08:58"],
]
]
];
New array data:
$newArr = [
"A" => [
"color" => 'red',
"user" => [
"Fabian" => ["06:03:00"], // + 1 user
"Max" => ["04:10:12"], // + 1 new time
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52", "14:05:32", "20:34:15"], // an older one is available, + 2 new times
"Hugo" => ["04:10:12"], // + 1 time
]
],
"C" => [ // + new whole group
"color" => 'green',
"user" => [
"Maxi" => ["07:08:58", "04:10:12"],
]
]
];
Supplement the existing data with the new data:
echo '<pre>';
print_r(array_merge($existingtArr, $newArr));
echo '</pre>';
Expected result array data:
$resultArr = [
"A" => [
"color" => 'red',
"user" => [
"Daniel" => ["01:18:08", "04:10:12"],
"Fabian" => ["06:03:00"],
"Max" => ["01:04:00", "04:10:12"],
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52", "14:05:32", "20:34:15"],
"Hugo" => ["07:08:58", "04:10:12"],
]
],
"C" => [
"color" => 'green',
"user" => [
"Maxi" => ["07:08:58", "04:10:12"],
]
]
];
You cannot simply call array_merge_recursive() on the whole data sets because they will generated repeated color values, but you want the color values to remain singular and the user data to be recursively merged.
To accommodate this logic (assuming it is okay to simply mutate the $existingtArr array with the data from $newArr), perform a check for the existence of each letter-set, and either push the whole set for a non-present letter, or recursively merge the shared letter-sets.
Code: (Demo)
foreach ($newArr as $letter => $set) {
if (!isset($existingtArr[$letter])) {
$existingtArr[$letter] = $set;
} else {
$existingtArr[$letter]['user'] = array_merge_recursive(
$existingtArr[$letter]['user'],
$set['user']
);
}
}
var_export($existingtArr);

How to add unique identifier to each level of nested array with unknown values?

I have array that contains 4 levels of nesting:
$data = [
"" => [
'sub_level_1' => [
'sub_level_2' => [
'sub_level_3' => [
0 => ""
1 => "val"
]
]
]
],
"top_level_2" => [
'sub_level_1' => [
'sub_level_2' => [
'sub_level_3' => [
0 => ""
1 => "some_val"
2 => "foo"
]
]
]
]
];
I want to add other unique identifier for each nested level, so I use a counter on each level:
$top_level_counter = 0;
$sub_level_1_counter = 0;
$sub_level_2_counter = 0;
$sub_level_3_counter = 0;
foreach ($data as &$top_level)
{
$top_level['top_level_id'] = $top_level_counter++;
foreach($top_level as &$sub_level_1)
{
$sub_level_1['sub_level_1_id'] = $sub_level_1_counter++;
foreach($sub_level_1 as &$sub_level_2)
{
$sub_level_2['sub_level_2_id'] = $sub_level_2_counter++;
// and continue with the last level..
}
}
}
But as the example above, the keys can be empty ("") or even null because that's data from the database.
I end up getting errors related to invalid values, but I can't find out which.
I did figure that as I go inside each level, I have to check for:
if (is_array($sub_level_<x>)
because on each level above I already store the unique id which is not an array. But still, even with that, I get invalid input for foreach for example.
But I can't find out what's causing that, though I suspect it might be because of a null or empty value
Is there a better way to do that?
The reason I want to do it is to have an easier to work with identifier for each element in the array because the actual data is strings in another language.
Expected result would be:
$data = [
"" => [
'top_level_id' => 0,
'sub_level_1' => [
'sub_level_1_id' => 0,
'sub_level_2' => [
'sub_level_2_id' => 0,
'sub_level_3' => [
0 => ""
1 => "val"
]
]
]
],
"top_level_2" => [
'top_level_id' => 1,
'sub_level_1' => [
'sub_level_1_id' => 1,
'sub_level_2' => [
'sub_level_2_id' => 1,
'sub_level_3' => [
0 => ""
1 => "some_val"
2 => "foo"
]
]
]
]
];

Compare some keys from 2 multidimensional arrays

I need to compare the value of the keys from 2 multidimensional arrays :
array1 [
0 => [
"designation" => "multiple"
"type" => "AAAAA"
"model" => "B"
"isSim" => false
"order" => 5
]
etc...
]
array2 [
0 => [
"designation" => "single"
"type" => "AACAA"
"model" => "B"
]
etc...
]
I would like to compare 'designation', 'type', 'model' from array1 to array2 and if the values are the same, I will set the 'isSim' to true.
I know how to set 'isSim' but I've got some difficulties to compare the 2 multidimensional arrays
NOTE => The 2 arrays don't have the same size
Should be something like the following:
$basearray = [["designation" => "multiple",
"type" => "AAAAA",
"model" => "B",
"isSim" => false,
"order" => 5 ]];
$compareto = [["designation" => "single"
"type" => "AACAA"
"model" => "B"]];
foreach($basearray as $base){
foreach($compareto as $compare){
if($compare["designation"] == $base["designation"] &&
$compare["type"] == $base["type"] &&
$compare["model"] == $base["model"]){
$base["isSim"] = true;
}
}
}
Additionally if you are sure that there can only be one array in $compareto that is the same as an array from $basearray you can put a break; after $base["isSim"] = true;.

Removing an element from a nested array in PHP

Am working on a Laravel application whereby I have an associative array that am to pass to an API endpoint, Before posting to the API, I want to delete the img key together with its value . I have tried to use unset function but it is not removing the img key
Array where I want to remove the image property
$a[] = [
0 => array:4 [
"name" => "Martoo nnn"
"relationship" => "Spouse"
"dob" => "2001-02-03"
"img" => "img.png"
]
1 => array:4 [
"name" => "sdsdsd sdsdsd"
"relationship" => "Child"
"dob" => "2019-04-04"
"img" => "img1.png"
]
2 => array:4 [
"name" => "sdsdsd sddds"
"relationship" => "Child"
"dob" => "2019-04-05"
"img" => "img2.png"
]
3 => array:4 [
"name" => "dssdsd dsdsd"
"relationship" => "Child"
"dob" => "2019-04-02"
"img" => "img3.png"
]
4 => array:4 [
"name" => "dssdsd dssdsd"
"relationship" => "Child"
"dob" => "2019-04-04"
"img" => "img4.png"
]
];
Unset method
$array = $a;
unset($array['img']);
//dd($a);
You can do something like this,
foreach ($array as $key => &$value) { // & defines changes will be made # value itself
unset($value['img']);
}
And Yes, I don't understand why you initialised $a as $a[]?
$newarray = array_filter($a, function($k) {
return $k != 'img';
}, ARRAY_FILTER_USE_KEY);
and pass this new array

Laravel collection contains

I'm using the Laravel contains method on a collection https://laravel.com/docs/5.3/collections#method-contains. But it does not work for me.
foreach ($this->options as $option) {
if($options->contains($option->id)) {
dd('test');
}
}
dd($options); looks like this:
Collection {#390
#items: array:1 [
0 => array:3 [
0 => array:7 [
"id" => 10
"slug" => "test"
"name" => "test"
"poll_id" => 4
"created_at" => "2016-11-12 20:42:42"
"updated_at" => "2016-11-12 20:42:42"
"votes" => []
]
1 => array:7 [
"id" => 11
"slug" => "test-1"
"name" => "test"
"poll_id" => 4
"created_at" => "2016-11-12 20:42:42"
"updated_at" => "2016-11-12 20:42:42"
"votes" => []
]
2 => array:7 [
"id" => 12
"slug" => "test-2"
"name" => "test"
"poll_id" => 4
"created_at" => "2016-11-12 20:42:42"
"updated_at" => "2016-11-12 20:42:42"
"votes" => []
]
]
]
}
Result of dd($option->id); is 10.
What could be wrong? Or is there a better way?
You should pass a key / value pair to the contains method, which will
determine if the given pair exists in the collection. Use contains() method in this way:
foreach ($this->options as $option) {
// Pass key inside contains method
if($option->contains('id', $option->id)) {
dd('test');
}
}
Use the following, which tells Laravel you want to match the 'id':
$options->contains('id', $option->id);
Docs
foreach ($this->options as $option) {
if(!$options->flatten(1)->where('id',$option->id)->isEmpty()) {
dd('test');
}
}
The contains method determines whether the collection contains a given item.
There are basically three ways in which it can be used :
simply checking the item
$collection = collect(['name' => 'Sarah', 'age' => 23]);
$collection->contains('Desk');
// false
$collection->contains('Sarah');
// true
checking the key/value pair :
$collection = collect([
['name' => 'Sarah', 'age' => 23],
['name' => 'Vicky', 'age' => 34],
]);
$collection->contains('name', 'Hank');
// false
checking via callback function :
$collection = collect([3, 5, 7, 9, 11]);
$collection->contains(function ($value, $key) {
return $value < 2;
});
// false
Now, for your problem, we will use the second category, i.e :
foreach ($this->options as $option) {
// here id is key and $option->id is value
if($option->contains('id', $option->id)) {
dd('test');
}
}
Link to docs

Categories