Get certain key and it's value in multidimentional array in php - php

Suppose I have this array,
$cast = [
'jon' => [
'fullname' => 'Jon Snow',
'class' => 'warrior',
],
'margery' => [
'fullname' => 'Margery Tyell',
'class' => 'politician'
]
];
How do I get the key and it's certain value only? like this,
$name = ['jon'=>'Jon Snow', 'margery'=>'Margery Tyrell'];
Is there any function that support this, so It doesn't have to be loop ?
Any answer will be appreciated!

You can iterate through the multidimensional array, and add the key and the value at index fullname of the inner array in a new one-dimensional array like this:
$names = [];
foreach ($cast as $character => $character_details) {
$names[$character] = $character_details['fullname'];
}
EDIT Alternatively, if you don't want to loop through the elements, you could use array_map function which takes a function as an argument where you can specify how to map each element of the array. In your case, you would simply return the fullname of an element.
$names = array_map(
function ($value) {
return $value['fullname'];
},
$cast
);

I guess that you have iterate over it and extract only interesting you keys. Here you have an example:
function getValuesForKey($array, $key){
$result = [];
foreach($array as $k => $subarray){
if(isset($subarray[$key])){
$result[$k] = $subarray[$key];
}
}
return $result;
}

Related

Filter and merge data between two 2d arrays - result must not show rows that occur more than once in its original array

How do I get the elements from the two arrays below where the value of email key only exists once from either of the list, and then merge their keys?
Say, I got two arrays:
$arr1 = [
['email' => 'aa#gmail.com', 'name' => 'John Doe'],
['email' => 'bb#gmail.com', 'name' => 'Johnny Sins'],
['email' => 'cc#gmail.com', 'name' => 'Jose Alvarado']
];
$arr2 = [
['email' => 'cc#gmail.com', 'country' => 'Japan'],
['email' => 'cc#gmail.com', 'country' => 'China'],
['email' => 'bb#gmail.com', 'country' => 'Korea'],
];
The final result should be:
[
['email' => 'aa#gmail.com', 'name' => 'John Doe'],
['email' => 'bb#gmail.com', 'name' => 'Johnny Sins', 'country' => 'Korea'],
];
I tried the following, but I'm stuck on merging the key:
$merged = array_merge($arr1, $arr2);
$result = array_filter($merged, function($value) use($merged) {
return array_count_values(array_column($merged, 'email'))[$value['email']] < 3;
});
My approach used in this demo was first determining on both arrays if there are any duplicated email. Such check will produce an array containing all the emails that shouldn't be taken into account as the marge of the duplicated emails coming from both arrays.
Then you can use such array when filtering the first and second array so that it will be returned only the entries NOT having any email contained in the above cited array of not allowed emails.
In the end it's a matter of returning the filtered arrays merged.
References:
https://www.php.net/manual/en/function.array-column
https://www.php.net/manual/en/function.array-count-values
https://www.php.net/manual/en/function.array-filter
https://www.php.net/manual/en/function.array-keys
https://www.php.net/manual/en/function.in-array
https://www.php.net/manual/en/function.array-merge
Demo:
https://onlinephp.io/c/c638f
<?php
$arr1 = [
['email' => 'aa#gmail.com', 'name' => 'John Doe',],
['email' => 'bb#gmail.com', 'name' => 'Johnny Sins',],
['email' => 'cc#gmail.com', 'name' => 'Jose Alvarado',],
];
$arr2 = [
['email' => 'cc#gmail.com', 'country' => 'Japan',],
['email' => 'cc#gmail.com', 'country' => 'China',],
['email' => 'bb#gmail.com', 'country' => 'Korea',],
];
//returns an array with emails being duplicated in $arr
function getDuplicatedEmails($arr){
//gets list of emails found from entries in $arr
$emails = array_column($arr, 'email');
//gets how many time each email is occurring in $emails
$emailsWithCounts = array_count_values($emails);
//filteres the above array returning only values with repetitions
$duplicates = array_filter($emailsWithCounts, function($value){ return $value > 1; });
//returns the emails found
return array_keys($duplicates);
}
//returns an array like $arr minus the entries having [email] included in $notAllowed
function filterByEmailNotAllowed($arr, $notAllowed = []){
//filter the list of emails
$filtered = array_filter($arr, function ($item) use ($notAllowed) {
//return this item if the email is not included in $notAllowed
return !in_array($item['email'], $notAllowed);
});
return $filtered;
}
//merge the arrays arr1 and arr2 excluding entries having emails duplicated in any of the two input arrays
function merge($arr1, $arr2){
$emailNotAllowed1 = getDuplicatedEmails($arr1);
$emailNotAllowed2 = getDuplicatedEmails($arr2);
$emailNotAllowed = array_merge($emailNotAllowed1, $emailNotAllowed2);
$filter1 = filterByEmailNotAllowed($arr1, $emailNotAllowed);
$filter2 = filterByEmailNotAllowed($arr2, $emailNotAllowed);
$filter = array_merge($filter1, $filter2);
return $filter;
}
$result = merge($arr1, $arr2);
var_dump($result);
After misunderstanding your question, I've made another attempt. The complication is that you need to check for duplicates in each array and not the joint results.
This first checks for duplicates and creates a list of the ones that should be excluded...
function summarize($array) {
$arrCount = array_count_values(array_column($array, 'email'));
return array_filter($arrCount, function ($value) {
return $value > 1;
}); }
$result1 = summarize($arr1);
$result2 = summarize($arr2);
$jointDuplicates = array_merge($result1, $result2);
What it then does is it builds a list of the non-duplicate values, merging in new values as it encounters existing values, creating a new element where it doesn't...
$jointList = array_merge($arr1, $arr2);
$result = [];
foreach ($jointList as $value) {
$email = $value['email'];
if (isset($jointDuplicates[$email])) {
continue;
}
if (isset($result[$email]) === false) {
$result[$email] = [];
}
$result[$email] = array_merge($result[$email], $value);
}
print_r($result);
Until there is further clarification from the asker (and a better representing set of sample data), I am going assume:
The first array will not contain duplicated email values.
Related rows in the first array must be disqualified if duplicated (conflicting) values for a given email are found in the second array..
Populate a result array from the first array and assign tempoary, first-level associative keys so that the array can be used as a lookup while processing.
Populate another lookup array containing unique email values and their respective counts in the second array.
Iterate over the second array and either unset the related row in the result array if it has been disqualified or append the row's data to the corresponding result row.
Code: (Demo)
$result = array_column($arr1, null, 'email');
$counts = array_count_values(array_column($arr2, 'email'));
foreach ($arr2 as $row) {
if ($counts[$row['email']] > 1) {
unset($result[$row['email']]); // disqualify row because duplicated in arr2
} else {
$result[$row['email']] += $row; // append arr2 data to result data
}
}
var_export(array_values($result));
If either array may have disqualifying rows, then the following approach will sanitize and merge the two arrays on their shared email value with a low time complexity.
Code: (Demo)
function sanitizeAndKey($array) {
$found = [];
$clean = [];
foreach ($array as $row) {
if (isset($found[$row['email']])) {
unset($clean[$row['email']]);
} else {
$found[$row['email']] = $row;
$clean[$row['email']] = $row;
}
}
return $clean;
}
$result = sanitizeAndKey($arr1);
foreach (sanitizeAndKey($arr2) as $key => $row) {
$result[$key] = ($result[$key] ?? []) + $row; // append arr2 data to arr1 data
}
var_export(array_values($result));

Creating Associative array (hardcoded key) from foreach in PHP

I have an array called $arr containing some information about users. Using $arr I want to create a new associative array with specific keys. That's what I got so far:
$groups = [];
foreach($arr as $val) {
$groups['first_key_name'] = $val->ID;
$groups['second_key_name'] = $val->login;
}
What I'm trying to achieve is a new array that has the following format:
'first_key_name' => $val->ID
'second_key_name' => $val->login
'first_key_name' => $val->ID
'second_key_name' => $val->login
The problem with my current approach is when I var_dump($groups) I only get one key with an empty value although the array should contain at least 10 entries.
The output of var_dump($groups):
array:1 [▼
"first_key_name" => "4"
]
What am I doing wrong?
You are overwriting your variables each time round the loop in this code
$groups = [];
foreach($arr as $val) {
$groups['first_key_name'] = $val->ID;
$groups['second_key_name'] = $val->login;
}
So instead do
$groups = [];
foreach($arr as $val) {
$groups[] = [
'first_key_name' => $val->ID
'second_key_name' => $val->login
];
}
This will create something like this
[0]
[
'first_key_name' = 1,
'second_key_name' = 99
]
[1]
[
'first_key_name' = 2,
'second_key_name' = 199
]
etc
You approach is overwriting the key value every time. That's why you need to use 2d array.
You can try like this:
$groups = [];
foreach($arr as $val) {
$groups[] = ['first_key_name' => $val->ID, 'second_key_name' => $val->login];
}
What happens here, is you are overwriting first_key_name and second_key_name in each turn of the loop. But you want to get an array with the new key=>value pairs.
To achieve that you have to append a new item to your array called $groups, like this:
$groups = [];
foreach ($arr as $val) {
$groups[] = [
'first_key_name' => $val->ID,
'second_key_name' => $val->login
];
}
You may also use array_map for this:
$groups = array_map(function ($val) {
return [
'first_key_name' => $val->ID,
'second_key_name' => $val->login,
];
}, $arr);

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.

Loop over one array as if it was a multidimensional array

I have an array like:
$array = array(
'name' => 'Humphrey',
'email' => 'humphrey#wilkins.com
);
This is retrieved through a function that gets from the database. If there is more than one result retrieved, it looks like:
$array = array(
[0] => array(
'name' => 'Humphrey1',
'email' => 'humphrey1#wilkins.com'
),
[1] => array(
'name' => 'Humphrey2',
'email' => 'humphrey2#wilkins.com'
)
);
If the second is returned, I can do a simple foreach($array as $key => $person), but if there is only one result returned (the first example), I can't run a foreach on this as I need to access like: $person['name'] within the foreach loop.
Is there any way to make the one result believe its a multidimensional array?
Try this :
if(!is_array($array[0])) {
$new_array[] = $array;
$array = $new_array;
}
I would highly recommended making your data's structure the same regardless of how many elements are returned. It will help log terms and this will have to be done anywhere that function is called which seems like a waste.
You can check if a key exists and do some logic based on that condition.
if(array_key_exists("name", $array){
//There is one result
$array['name']; //...
} else {
//More then one
foreach($array as $k => $v){
//Do logic
}
}
You will have the keys in the first instance in the second yours keys would be the index.
Based on this, try:
function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
if(isAssoc($array)){
$array[] = $array;
}
First check if the array key 'name' exists in the given array.
If it does, then it isn't a multi-dimensional array.
Here's how you can make it multi-dimensional:
if(array_key_exists("name",$array))
{
$array = array($array);
}
Now you can loop through the array assuming it's a multidimensional array.
foreach($array as $key => $person)
{
$name = $person['name'];
echo $name;
}
The reason of this is probably because you use either fetch() or fetchAll() on your db. Anyway there are solutions that uses some tricks like:
$arr = !is_array($arr[0]) ? $arr : $arr[0];
or
is_array($arr[0]) && ($arr = $arr[0]);
but there is other option with array_walk_recursive()
$array = array(
array(
'name' => 'Humphrey1',
'email' => 'humphrey1#wilkins.com'
),
array(
'name' => 'Humphrey2',
'email' => 'humphrey2#wilkins.com'
)
);
$array2 = array(
'name' => 'Humphrey2',
'email' => 'humphrey2#wilkins.com'
);
$print = function ($item, $key) {
echo $key . $item .'<br>';
};
array_walk_recursive($array, $print);
array_walk_recursive($array2, $print);

Get two columns of data as an array of keys and values using Laravel

What is the Laravel's way to retrieve an associative array in which, the keys are the first column of a query and the values are the second column.
User::select('id','type')->unknown();
should return:
[
2=>'s',
30=>'t',
32=>'x',
]
It should be:
User::lists( 'type', 'id' )->all();
I don't think that method exists, but what you could do is use array_column on the returned associative array to get what you want:
$array = User::select( 'type', 'id' )->all();//get array of assoc arrays
$result = array_column($array, 'type', 'id');//column
this will return an array using the id key in each sub array of $array (ie each result/assoc array) as key, and the type value as value. So if $array looks like this:
$array = [
[
'id' => 1,
'type' => 'a',
],
[
'id' => 2,
'type' => 'b',
],
];
The result of the array_column call will look like this:
$result = [
1 => 'a',
2 => 'b',
];
Note array_column requires PHP 5.5 or higher, if you're running 5.4, and you can't upgrade, write your own function, it's easy enough:
function myArrayCol(array $in, $valKey, $idxKey = null)
{
$result = [];
foreach ($in as $sub) {
if (!is_array($sub)) {
throw new RuntimeException('myArrayCol requires a multi-dimensional array to be passed');
}
$value = isset($sub[$valKey]) ? $sub[$valKey] : null;
if ($idxKey === null || !isset($sub[$idxKey])) P
$result[] = $value;
} else {
$result[$sub[$idxKey]] = $value;
}
}
return $result;
}
Note this implementation is completely untested, but you get the basic idea...
Update
Aparently, laravel does have a method that does what the OP wants, as #Mat suggested, and the docs show:
$result = User::lists('type', 'id')->all();
That returns the result the OP is after in a one-liner.

Categories