Converting a multidimensional array into a single dimensional one - php

If from a function I am returned a multidimensional array like this..
array(0 => array('a' => 5), 1 => array('a' => 8))
But I just really need the contents of the key 'a' what is the best way for me to convert.
Current I have been doing something like..
$new_array = array();
foreach ($multi_array AS $row) {
$new_array[] = $row['a']
}

without foreach:
$array = array(0 => array('a' => 1), 1 => array('a' => 8));
$new_array = array_reduce($array, function ($result, $current) {$result[]=current($current); return $result;}, array());

If that is all your requirements are, I think that is the best way. Anything else will have the same processing. Even after looking through the Array functions, I would say that this would be the best way. You can, however, make it a function to make it a bit more versatile:
$array = array(0 => array('a' => 1), 1 => array('a' => 8));
$new_array = flatten($array, 'a');
function flatten($array, $index='a') {
$return = array();
if (is_array($array)) {
foreach ($array as $row) {
$return[] = $row[$index];
}
}
return $return;
}
But yea, I would say what you have would be the most efficient way of doing it.

You Can Try As Like Following ......
$multi_array = array(0 => array('a' => 5), 1 => array('a' => 8));
$new_array = array();
foreach ($multi_array AS $key => $value) {
$new_array[] = $value['a'];
}

I recently found myself facing this problem, and I believe I have found the solution.
I will go over the problem itself, and also the solution, trying to explain everything along the way.
The problem was that I didn't have a two-dimensional array, but an array which could have any number of arrays inside arrays, so the solution by Brad F Jacobs couldn't apply here, although it's very simple and functional.
I had to work with a self-referencing database table called 'webpage', where one of the columns was 'parentWebpageId', which referenced an Id of some other row in that same table. This way, a tree structure can be built and easily managed, if you get your loops right.
Ia easily made a function which is supposed to generate a multi-dimensional array from one-dimensional self-referencing array, but the problem arose when I tried to make a function which should to the opposite. I needed this because if I wanted to delete a certain webpage, all it's children should also be deleted, in order to preserve the self-referential integrity.
It was easy to generate a tree whose root was the page that was initially to be deleted, but then I needed a list of all child webpage's Ids, in order to delete all of them.
So, the structure I had was something like this:
webpage1
id
title
...
childWebpageArray
webpage2
id
title
...
childWebpageArray
webpage2.1
id
url
...
childWebpageArray
webpage2.2
id
url
...
childWebpageArray
webpage2.2.1
id
url
...
childWebpageArray
webpage2.2.2
id
url
...
childWebpageArray
webpage2.3
id
url
...
childWebpageArray
webpage3
id
title
...
childWebpageArray
As you can see, the depth can go forever.
What I came up with is this:
function flattenMultidimensionalArray($multidimensionalArray) {
// Set anchor.
ohBoyHereWeGoAgain:
// Loop through the array.
foreach ($multidimensionalArray as $key1 => $value1) {
// Check if the webpage has child webpages.
if (isset($multidimensionalArray[$key1]["childWebpageArray"]) && (count($multidimensionalArray[$key1]["childWebpageArray"]) > 0)) {
// If it does, loop through all the child webpages, and move them into the initial multi-dimensional array.
foreach ($multidimensionalArray[$key1]["childWebpageArray"] as $key2 => $value2) {
$multidimensionalArray[] = $multidimensionalArray[$key1]["childWebpageArray"][$key2];
}
// Unset element's child pages, because all those child pages, along with their child pages
// have been moved into the initial array, thus reducing the depth of a multi-dimensional array by 1.
unset($multidimensionalArray[$key1]["childWebpageArray"]);
}
}
// Loop once again through the whole initial array, in order to check if any of the pages has children
foreach ($multidimensionalArray as $key => $value) {
// If a page which has children is found, kick the script back to the beginning, and go through it again.
if (isset($multidimensionalArray[$key]["childWebpageArray"]) && (count($multidimensionalArray[$key]["childWebpageArray"]) > 0)) {
goto ohBoyHereWeGoAgain;
}
}
// In the end, when there are no more pages with children, return an array of pages.
return $multidimensionalArray;
}
This solution worked in my case, and I believe is the right one for this kind of problem. It probably isn't much of a hassle to change it in order to fit your particular needs.
Hope this helps!

Related

Convert multi-dimensional array into matrix-like array

I have the following array:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany")
);
I need a matrix that crosses all the elements of the array; e.g:
array(
array("2018","Jan","France"),
array("2018","Jan","Germany"),
array("2019","Jan","France"),
array("2019","Jan","Germany")
);
meaning, 2 x 2 x 1 arrays
but this can be that I have more elements that are or are not arrays then:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany"),
array("prod1","prod2","prod3"),
'Act'
);
In this case I would get 2 x 2 x 1 x 4 x 1 arrays in the end.
Any idea on how to achieve this?
Is that what you are looking for ?
$dst = array(array());
foreach ($array as $idx => $val) {
foreach ($dst as $tmp_idx => $tmp_array) {
if (is_array($val)) {
foreach ($val as $sub_idx => $sub_val) {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $sub_val));
}
} else {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $val));
}
unset($dst[$tmp_idx]);
}
}
I declare the array with an empty array in it.
A first foreach iterates through the main array to access all the categories, whatever their number.
A second foreach iterates through the result array.
A third foreach - or not, depending if the category contains several or a single entry - iterates through each entry of the current category and merges an array containing only that current category in the result currently iterated on (from $dst). The merge is pushed into the result array. The result stored in $dst are assumed to be temporary and will be copied and completed with each new value, making them progressively contain one entry from each category.
At the end of each iteration on $dst, the current temporary result is removed from the array since it has no purpose : if the foreach iterated on it, that means that it was incomplete.
At the end, the only entries in $dst that remain are these who weren't iterated on, meaning that every entry from each category were considered when they were created, meaning that they are complete.
I'm available for any clarification may the need arise.

Find which keys of separate arrays intersect using a function

Ok so I have two arrays, one is an input array full of data like :
$array1 = ["_token" => "62d46d4h6dfh841df8h", "sku62" => "3e", "name62" => "meh", "sku61" => "3e", "name61" => "mah", "sku64" => "3e", "name64" => "moh"]
The other holds simply id's: $array2 = [64, 74, 61]
edit for clarity: $array1 is a snippet of input from a post request i.e. $array1 = $request->all(); The numbers present within the keys of this array are unique Id's appended on form generation to distinguish between rows with multiple form elements.
Each row has an "update" checkbox also with an appended unique id. When ticked this id shows up in the request e.g. update64.
$array2 was populated by doing a foreach through the request, identifying the update string and isolating the id:
foreach ($array1 as $id => $value) {
$idInt = filter_var($id, FILTER_SANITIZE_NUMBER_INT);
$str = preg_replace('/[0-9]+/', '', $id);
if ($str === "update") {
array_push($array2, $idInt);
}
}
I want a solution that returns the elements from $array1 that have the appended ids found in $array2.
My own attempt looks like this:
$relevant_keys = function($key1, $key2) {
return ((preg_replace('/[0-9]+/', '', $key1) === $key2)) ? 1 : -1;
};
$filtered = array_intersect_ukey($array1, array_flip($array2), $relevant_keys);
However $filtered is returning empty and if I dd($key2) within the function it's not even returning an element from $array2, I get something from $array1 instead so this has left me confused.
Would appreciate any help.
Here's the solution to the exact problem you posted:
$filtered = [];
foreach ($array1 as $key => $value)
{
if ( ! preg_match('/(\d+)$/', $key, $matches)) continue;
if ( ! isset($matches[1]) || ! in_array($matches[1], $array2)) continue;
$filtered[$key] = $value;
}
But I'm not sure you're approaching this correctly. That input looks suspicious.
Are you sure there's no better way to format the request?
I have a few important insights to share based on your coding attempt.
array_intersect_ukey() should be the perfect function call for his task, but alas, it is not. I'll tell you why.
array_intersect_ukey() suffers in the same way as array_intersect_uassoc() and array_uintersect_uassoc() because the internal algorithm will stop looking for additional qualifying keys after it encounters its first one. I first came upon this reality here.
Also, the way that you've declared and used the custom function arguments ($key1 and $key2) indicates that you believe $key1 always relates to the first nominated array and $key2 always relates to the second nominated array. This is not true and I have seen many developers with this same false impression. The truth is that under the hood, the two parameters fed into the custom function may come from either array.
For the reasons in #1, I'll recommend that you shift your focus to array_filter(). By establishing a lookup array containing whitelisted keys and filtering on keys, you can swiftly filter your data. Inside the callback, I am using trim() to remove the letters before the id number at the end. This is just one way of isolating the whole number at the end of each key.
Code: (Demo)
$lookup = array_flip($array2);
var_export(
array_filter(
$array1,
fn($key) => isset($lookup[ltrim($key, 'a..z')]),
ARRAY_FILTER_USE_KEY
)
);
Output:
array (
'sku61' => '3e',
'name61' => 'mah',
'sku64' => '3e',
'name64' => 'moh',
)

Make a unique list of values from a particular key existing anywhere in a deep array

I have an array that consists of an undetermined number of arrays, recursively (n levels deep). Each array might contain a name key. I want to create a unique list of those values.
Example
Suppose the array is:
$bigArray = array(
'name'=>'one',
'something'=>array(
'name'=>'two',
'subthing'=>array('name'=>'three')
),
'anotherthing'=>array('name'=>'one')
);
The expected result would be:
$uniques = array('one', 'two', 'three') // All the 'name' keys values and without duplicates.
Here's a fiddle of my attempt.
My approach was using array_walk_recursive passing a $uniques array as reference, and allowing the function to update that value:
$uniques = array();
function singleOut($item, $key, &$uniques) {
if ($key == 'name' && !in_array($itm,$uniques,true) )
$uniques[] = $item;
}
array_walk_recursive($bigArray, 'singleOut', $uniques);
However, it's not working for me.
You could use also array_unique on this one too. Example:
$uniques = array();
array_walk_recursive($bigArray, function($val, $key) use (&$uniques){
if($key == 'name') {
$uniques[] = $val;
}
});
$uniques = array_unique($uniques); // unique values
Your fiddle was nearly spot on - the problem was, that the user parameter is given by-reference only within same levels of recursion. You need to use indirection with a reference:
$bigArray = array(
'name'=>'one',
'something'=>array(
'name'=>'two',
'subthing'=>array('name'=>'three')
),
'anotherthing'=>array('name'=>'one')
);
function singleOut($item, $key, $indirect) {
$uniques=&$indirect[0];
if ($key == 'name' && !in_array($item,$uniques,true) ) $uniques[] = $item;
}
$uniques = array();
$indirect = array(&$uniques);
array_walk_recursive($bigArray, 'singleOut', $indirect);
print_r($uniques);
Edit:
Fiddle is here
To avoid doing an in_array() check inside of array_walk_recursive(), you can store name values as keys in the output array. This will effectively eliminate duplicates by overwriting previous identical keys. When array_walk_recursive() is finished, you can use array_keys() to move the data from keys to values.
Code: (Demo)
$bigArray=[
'name'=>'one',
'something'=>[
'name'=>'two',
'subthing'=>['name'=>'three']
],
'anotherthing'=>['name'=>'one']
];
array_walk_recursive($bigArray,function($v,$k)use(&$uniques){
if($k==='name')
$uniques[$v]='';
});
var_export(array_keys($uniques));
Output:
array (
0 => 'one',
1 => 'two',
2 => 'three',
)
Because array_unique() can be slow in some cases, using array_keys() should generally perform faster. That said, if micro-optimization is a concern then you should do benchmark testing using your actual data and your specific environment and select the best method for your project.
As I mentioned in a comment under Ghost's answer, it is a good habit to make === strict comparisons on keys in your multi-dimensional array because if you are looking for a string, but encounter a 0 key, then PHP's type juggling "feature" will provide unexpected results.
Here is a page where I discuss and demonstrate this behavior: Type juggling while making loose comparison yields unwanted result

PHP remove arrays by nested key value

I have some arrays that I'd like to unset based on a key.
For example, let's say I have this array:
$array = array(
'one' => array('item' => '1'),
'two' => array('item' => '2')
);
If I want to unset the nested array with key 'two', I could do:
unset($array['two'])
or if I wanted to unset just the item array for key 'two', I could do:
unset($array['two']['item'])
I want to dynamically delete array items based on known keys. So for example, I know I want to delete ['two']['item'].
How do I pass those two arguments to a method, that can then be appended to an array?
Example:
//This works fine if it's only the first item in the array
function deleteArray($keys)
{
unset($this->array[$keys]);
}
But when we want to delete nested items, this would not work. I could pass in the keys as an array such as array('two', 'item') and build the index off of this, but not sure how....
Any help would be great! thank you!
You can use this function:
function delete(&$array, $keys)
{
$key = array_shift($keys);
if (count($keys) == 0)
unset($array[$key]);
else
delete($array[$key], $keys);
}
Try with a recursive function:
function deleteArray(&$array, $keys) {
if ( count($keys) == 1 )
unset( $array[$keys[0]] );
else
{
$k = array_shift($keys);
deleteArray($array[$k],$keys);
}
}
deleteArray($this->arr, array("three","item","blabla")); // This erase $this->array["three"]["item"]["blabla"]
function deleteArray($keys)
{
$keyarray = explode($keys, " ");
unset($this->array[$keyarray[0]][$keyarray[1]]);
}
I edited it a bit (won't work!) maybe somebody could continue that. Maybe it is possible with a while() ...

How do I perform an effective array search in PHP when I'm looking for more than one key in the array?

Taking a dictionary as an example, let's say I've got the following array to catalog the pronunciations of a given word:
$pronunciation["text"] = array("alpha", "beta", "gamma", "delta", "epsilon");
$pronunciation["definition"] = array(1, 2, NULL, NULL, 1);
text contains the pronunciation that will be displayed to the user, while definition contains the ID for the definition in the list where that pronunciation applies. If definition is NULL, it's not associated with any particular definition, and should be displayed elsewhere in the entry.
Now, if I try to do something like the following:
$key = array_search(1, $pronunciation["definition"]);
All I'll get is 0, since that's the first key in definition that contains the value. Likewise, substituting NULL returns 3, however both of these actually have another related key that I'm trying to fetch.
Is there a way to get it to return all related keys without having to resort to brute force methods such as a for loop?
Try this one:
array_keys($pronunciation["definition"],1)
I'm afraid there is not a function that do that as you want. Edit: Nope, there is! See Jauzsika answer.You have to use a foreach or a for loop.
function array_search_all($needle, $haystack) {
$keys = array();
foreach ($haystack as $k => $v) if ($v === $needle) $keys[] = $k;
return $keys;
}
Call:
$keys = array_search_all(1, $pronunciation["definition"]);
I don't know wheter it's faster or not, but I think if there're a set of data which you have to search in, it's better to set up an array, where the data is the key.
$data["alpha"] = 1
$data["beta"] = 1
//...
All your foreach should be modified from
foreach ($data as $item) {
to
foreach ($data as $item => $value) {
Restructure your arrays into a single array keyed on the text and using the definition as the value:
$pronunciation = array("alpha" => 1,
"beta" => 2,
"gamma" => NULL,
"delta" => NULL,
"epsilon" => 1);
Then use array_filter() to build a subset of the values you need
$searchVal = 1;
$subset = array_filter($pronunciation,
create_function('$value', 'return $value == '.$searchVal.';')
);
var_dump($subset);

Categories