php - How do I weed out similar items from an associative array? - php

UPDATE: someone edited my question incorrectly, but it's fixed now
Let's say I have the following associative array:
Array (
[Postponement no issue for Man Utd (BBC) ] => 8
[Postponement no issue for Man Utd ] => 7
[Postponement no issue for Man Utd: Manchester United say they have no issue over Sunday's game at Chelsea being ... ] => 3
)
You'll notice that the term "Postponement no issue for Man Utd" appears in all three keys. I want to turn this array into a new array like this:
Array ( [Postponement no issue for Man Utd] => 18 )
Where 18 is the sum of all values where the key contains "Postponement no issue for Man Utd".
In other words: if one key appears inside another key, the latter is dropped fom the new array and it's value is added on to the former key's value.
Is this possible and how? Thanks in advance.

Depending on the size of your data this may not be feasible. There is almost certainly a more optimal solution, but this works:
<?php
$array = array(
'Postponement no issue for Man Utd (BBC)' => 8,
'Postponement no issue for Man Utd' => 7,
'Postponement no issue for Man Utd: Manchester United say they have no issue over Sunday\'s game at Chelsea being ...' => 3
);
$keys = array_keys($array);
usort($keys, 'lengthCmp');
$flippedKeys = array_flip($keys);
$data = array();
foreach ($keys as $k => $v)
{
$sum = 0;
foreach ($array as $key => $val)
{
if (stripos($key, $v) !== FALSE)
{
$sum += $val;
unset($array[$key]);
unset($keys[$flippedKeys[$v]]);
}
}
$data[$v] = $sum;
}
foreach ($data as $key => $val)
{
if ($val == 0)
unset($data[$key]);
}
var_dump($data);
function lengthCmp($a, $b)
{
return strlen($a) - strlen($b);
}
?>
Output:
array(1) {["Postponement no issue for Man Utd"] => int(18)}
And with this data set:
$array = array(
'test test' => 1,
'Postponement no issue for Man Utd (BBC)' => 8,
'Postponement no issue for Man Utd' => 7,
'Postponement no issue for Man Utd: Manchester United say they have no issue over Sunday\'s game at Chelsea being ...' => 3,
'test' => 1,
'not here' => 1,
'another test' => 1
);
Output:
array(3) {
["test"] => int(3)
["not here"] => int(1)
["Postponement no issue for Man Utd"] => int(18)
}

foreach($array as $k1 => $v1)
foreach($array as $k2 => $v2)
if(strpos($k2, $k1) !== false) // or === 0
$result[$k1] = (isset($result[$k1]) ? $result[$k1] : 0) + $v2;
untested

A simple approach could be:
$original_array = array(...); // The original array with keys and values
asort($original_array); // Sort the array by its keys
$clean_array = array();
foreach ($original_array as $key => $value)
{
foreach (array_keys($clean_array) as $clean_key)
{
$found = false;
if (preg_match('/'.preg_quote($clean_key).'/', $key))
{
$clean_array[$clean_key] += $value;
$found = true;
break;
}
if (!$found) $clean_array[$key] = $value;
}
}

Ok... the only way to do this is to iterate through the array twice, and have a function compare them:
foreach ($my_array as $key1 => $value1) {
foreach ($my_array as $key2 => $value2) {
if ($key1 != $key2) { // don't compare them if they're the same
if (compareKeys($key1,$key2)) {
$my_array[$key1] += $value2;
unset($my_array[$key2]);
}
}
}
}
Then your compareKeys function could look something like this:
function compareKeys($val1,$val2) {
return (strpos($val1,$val2)!==false);
}

Related

Group associative array data by key prefix

I have a array like this
Array (
[operator_15] => 3
[fiter_15] => 4
[operator_17] => 5
[fiter_17] => 5
[operator_19] => 4
[fiter_19] => 2
)
I want to separate this array in to 2 arrays:
key starting from fiter_
key starting from operator_
I used array filter and it doesn't work. any other option?
$array = array_filter(
$fitered_values,
function($key) {
return strpos($key, 'fiter_') === 0;
}
);
Just loop the array and substring what is before the _ with strpos and substr then you can filter them to a new array as this.
This method will also work with new array keys, see example:
$arr = array ( "operator_15" => 3,
"fiter_15" => 4,
"operator_17" => 5,
"fiter_17" => 5,
"somethingelse_12" => 99 // <--- Notice this line.
);
foreach($arr as $key => $val){
$subarr = substr($key,0, strpos($key, "_"));
$new[$subarr][$key] = $val;
}
var_dump($new);
output:
array(3) {
["operator"]=>
array(2) {
["operator_15"]=>
int(3)
["operator_17"]=>
int(5)
}
["fiter"]=>
array(2) {
["fiter_15"]=>
int(4)
["fiter_17"]=>
int(5)
}
["somethingelse"]=> // <-- is here now in it's own group with no code added
array(1) {
["somethingelse_12"]=>
int(99)
}
}
Give a try with below and see if its solve your problem
$array = array (
'operator_15' => 3,
'fiter_15' => 4,
'operator_17' => 5,
'fiter_17' => 5,
'operator_19' => 4,
'fiter_19' => 2 );
$operator=array();
$filter=array();
foreach($array as $key => $value){
if (strpos($key, 'operator_') !== false) {
$operator[$key] = $value;
}
if (strpos($key, 'fiter_') !== false) {
$filter[$key] = $value;
}
}
print_r($operator);
print_r($filter);
While iterating your array, populate a new array with first level (grouping) keys based on the prefix (substring before the underscore), then push the original associative data into that group.
Code: (Demo)
$result = [];
foreach ($array as $k => $v) {
$result[strtok($k, '_')][$k] = $v;
}
var_export($result);
It is suboptimal programming to declare individual variables because this removes the convenience of being able to easily iterate related data (related by structure).
The above snippet will allow you to iterate $result and access all data sets or you can individually access a particular subset like $result['fiter'].
This is a working example:
$a = array ( 'operator_15' => 3, 'fiter_15' => 4, 'operator_17' => 5, 'fiter_17' => 5, 'operator_19' => 4, 'fiter_19' => 2 );
$fiter_array = array();
$operator_array = array();
foreach($a as $key => $val)
{
if(strpos($key, 'fiter') !== false)
{
array_push($fiter_array, $a[$key]);
// or if you want to maintain the key
$fiter_array[$key] = $val;
}
else
{
array_push($operator_array, $a[$key]);
// or if you want to maintain the key
$operator_array[$key] = $val;
}
};
var_dump($fiter_array);
var_dump($operator_array);

How do I get the value of the first occurrence of array_walk_recursive in php

I have a deep multidimensional array that I am needing to extract the value of a specific key. I have found that the array_walk_recursive function will be my best option. I only need the first occurrence.
My array looks like this - (except much more complicated)
Array (
[vehicle info] => Array (
[one] => Array (
[submodel] => LX
[engine] => 2.3
)
[two] => Array (
[color] => blue
[year] => 2007
[wheels] => 4
)
[three] => Array (
[submodel] => LX
[make] => Ford
[model] => F-150
[offroad] => No
)
)
)
The issue here is, submodel is in both one and three. Additionally, the array is not consistent, so I must use array_walk_recursive to search through it for the matching key, then return the value for that key.
Here is my current code -
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted) {
echo ("$key is $item");
}
}));
The above returns submodel is LXsubmodel is LX.
Bonus Question!!
How can I search for multiple keys and return the first corresponding value for each of those? I was thinking putting all wanted keys in an array, then do a foreach loop, but don't quite know how to structure this. I am new to php.
array_walk_recursive() is the appropriate native function to call for this task. Keep track of which keys have already been declared in the result array and ensure that they are never overwritten.
Code: (Demo)
$needles = ['submodel', 'offroad'];
$result = [];
array_walk_recursive(
$array,
function($value, $key) use ($needles, &$result) {
if (
in_array($key, $needles)
&& !isset($result[$key])
) {
$result[$key] = "$key is $value";
}
}
);
var_export($result);
Output:
array (
'submodel' => 'submodel is LX',
'offroad' => 'offroad is No',
)
If your application has performance concerns, then the native function becomes less attractive because it will always iterate the entire input array's structure -- even after all sought keys are encountered. If you want to "break early" (short circuit), then you will need to design your own recursive function which will return when all sought keys are found.
Code: (Demo)
$soughtKeys = array_flip(['submodel', 'offroad']);
function earlyReturningRecursion(array $array, array $soughtKeys, array &$result = []): array
{
foreach ($array as $key => $value) {
if (!array_diff_key($soughtKeys, $result)) { // check if result is complete
return $result;
} elseif (is_array($value)) {
earlyReturningRecursion($value, $soughtKeys, $result);
} elseif (isset($soughtKeys[$key]) && !isset($result[$key])) {
$result[$key] = "$key is $value";
}
}
return $result;
}
var_export(earlyReturningRecursion($array, $soughtKeys));
// same output as the first snippet
I would start by setting the values you want to null, and then only saving them if they haven't been found yet, by checking is_null(). I haven't tested this code, but it should look something like this:
$submodel = null;
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted && is_null($submodel)) {
echo ("$key is $item");
$submodel = $item;
}
}));
array_walk_recursive() has the defect of not allowing to return matching results however in PHP 7 you could use an anonymous function and a variable to store the matching value.
$matching = null;
$wanted = "submodel";
array_walk_recursive ($array, function ($item, $key) use ($wanted, $matching) {
if (($key === $wanted) && is_null($matching)) {
$matching = $item;
}
});
As far as there is no way to return early from array_walk_recursive(), I'd suggest to create a function to find the first occurrence of $wanted:
$arr = [
'vehicle info' => [
'one' => ['submodel' => 'LX', 'engine' => '2.3'],
'two' => ['color' => 'blue', 'year' => '2007', 'wheels' => '4'],
'three' => ['submodel' => 'LX', 'make' => 'Ford', 'model' => 'F-150', 'offroad' => 'No'],
],
];
function find($needle, $haystack, $found = '')
{
foreach ($haystack as $key => $value) {
if ($found) {
break;
}
if ($key === $needle) {
$found = "{$needle} is {$value}";
break;
}
if (is_array($value)) {
$found = find($needle, $value, $found);
}
}
return $found;
}
$wanted = 'submodel';
$result = find($wanted, $arr);
var_dump($result); // string(14) "submodel is LX"
Live demo
Update: to search for multiple keys you'll need to do it in a loop:
$multiple_keys = array('submodel', 'year');
foreach ($multiple_keys as $wanted) {
var_dump(find($wanted, $arr));
}
// Output:
// string(14) "submodel is LX"
// string(12) "year is 2007"
Live demo

Search in a multidimensional assoc array

I have an array, looking like this:
[lund] => Array
(
[69] => foo
)
[berlin] => Array
(
[138] => foox2
)
[tokyo] => Array
(
[180] => foox2
[109] => Big entrance
[73] => foo
)
The thing is that there were duplicate keys, so I re-arranged them so I can search more specifically, I thought.
Previously I could just
$key = array_search('foo', $array);
to get the key but now I don't know how.
Question: I need key for value foo, from tokyo. How do I do that?
You can get all keys and value of foo by using this:
foreach ($array as $key => $value) {
$newArr[$key] = array_search('foo', $value);
}
print_r(array_filter($newArr));
Result is:
Array
(
[lund] => 69
[tokyo] => 109
)
If you don't mind about the hard code than you can use this:
array_search('foo', $array['tokyo']);
It just a simple example, you can modify it as per your requirement.
Try this
$a = array(
"land"=> array("69"=>"foo"),
"land1"=> array("138"=>"foo1"),
"land2"=> array('180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'),
);
//print_r($a);
$reply = search_in_array($a, "foo");
print_r($reply);
function search_in_array($a, $search)
{
$result = array();
foreach($a as $key1 => $array ) {
foreach($array as $k => $value) {
if($value == "$search") {
array_push($result,"{$key1}=>{$k}");
breck;
}
}
}
return $result;
}
This function will return the key or null if the search value is not found.
function search($searchKey, $searchValue, $searchArr)
{
foreach ($searchArr as $key => $value) {
if ($key == $searchKey && in_array($searchValue, $value)) {
$results = array_search($searchValue, $value);
}
}
return isset($results) ? $results : null;
}
// var_dump(search('tokyo', 'foo', $array));
Since Question: I need key for value foo, from tokyo. How do i do that?
$key = array_search('foo', $array['tokyo']);
As a function:
function getKey($keyword, $city, $array) {
return array_search($keyword, $array[$city]);
}
// PS. Might be a good idea to wrap this array in an object and make getKey an object method.
If you want to get all cities (for example to loop through them):
$cities = array_keys($array);
I created solution using array iterator. Have a look on below solution:
$array = array(
'lund' => array
(
'69' => 'foo'
),
'berlin' => array
(
'138' => 'foox2'
),
'tokyo' => array
(
'180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'
)
);
$main_key = 'tokyo'; //key of array
$search_value = 'foo'; //value which need to be search
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach ($iterator as $key => $value) {
$keys = array();
if ($value == $search_value) {
$keys[] = $key;
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
$keys[] = $iterator->getSubIterator($i)->key();
}
$key_paths = array_reverse($keys);
if(in_array($main_key, $key_paths) !== false) {
echo "'{$key}' have '{$value}' value which traverse path is: " . implode(' -> ', $key_paths) . '<br>';
}
}
}
you can change value of $main_key and $serch_value according to your parameter. hope this will help you.
<?php
$lund = [
'69' => 'foo'
];
$berlin = [
'138' => 'foox2'
];
$tokyo = [
'180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'
];
$array = [
$lund,
$berlin,
$tokyo
];
echo $array[2]['180']; // outputs 'foox2' from $tokyo array
?>
If you want to get key by specific key and value then your code should be:
function search_array($array, $key, $value)
{
if(is_array($array[$key])) {
return array_search($value, $array[$key]);
}
}
echo search_array($arr, 'tokyo', 'foo');
try this:
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'On');
$array=array("lund" => array
(
69 => "foo"
),
"berlin" => array
(
138 => "foox2"
),
"tokyo" => array
(
180 => "foox2",
109 => "Big entrance",
73 => "foo"
));
function search($array, $arrkey1, $arrvalue2){
foreach($array as $arrkey=>$arrvalue){
if($arrkey == $arrkey1){
foreach($arrvalue as $arrkey=>$arrvalue){
if(preg_match("/$arrvalue/i",$arrvalue2))
return $arrkey;
}
}
}
}
$result=search($array, "tokyo", "foo"); //$array=array; tokyo="inside array to check"; foo="value" to check
echo $result;
You need to loop through array, since its 2 dimensional in this case. And then find corresponding value.
foreach($arr as $key1 => $key2 ) {
foreach($key2 as $k => $value) {
if($value == "foo") {
echo "{$k} => {$value}";
}
}
}
This example match key with $value, but you can do match with $k also, which in this case is $key2.

How to make a associative array from other associative array?

I need some help with another PHP problem I am working on. I won't be posting the exact question, as I'd prefer to try and apply the knowledge I get from here to solve my problem.
First:
I have an associative array. I must loop through the array to find the array values which have keys that begin with a specific string and push both the key and value to an output array.
eg:
- Loop through the below array & push all elements which have a key beginning with "edible_" to an output array:
$assoc_array = array(
'edible_fruit' => 'apple',
'animal' => 'cat',
'edible_veg' => 'pumpkin',
'number' => '23',
'city' => 'Cape Town',
'edible_berry' => 'blueberry',
'color' => 'blue'
);
Would this work?
$result = array();
foreach ($assoc_array as $key => $value) {
if (substr($key, 0, 7) == "edible_") {
array_push($result, $value);
}
}
print_r($result);
Second:
How would I remove "edible_" from the output array's keys? With this second bit I have no idea where to even begin!
Third:
I've managed to figure out the above with all your help, thank you! Now I just need to find out how I would print each element on a new line with a date & timestamp at the end of each line? I've got this (doesn't seem to be working):
while (list($key, $value) = each($output)) {
print_r("$key => $value" . date("y/m/d G.i:s", time()) . "<br>");
}
First of your code will work.
To remove edible_ from the key you could use explode() -
$keyParts = explode('_', $key);
$newKey = $keyParts[1];
You will have to add the new keys to the array, which you're not doing now.
foreach ($assoc_array as $key => $value) {
if (substr($key, 0, 7) == "edible_") {
$keyParts = explode('_', $key);
$newKey = $keyParts[1];
$result[$newKey] = $value;
}
}
This would be my approach:
foreach($assoc_array as $key => $value) {
if(preg_match("/^edible_/",$key)) {
$result[preg_replace("/^edible_/","",$key)] = $value;
}
}
use preg_match to check if the key starts with what you are looking for and use preg_replace to remove the string from the beginning (^) of the key :)
Input ($assoc_array):
Array
(
[edible_fruit] => apple
[animal] => cat
[edible_veg] => pumpkin
[number] => 23
[city] => Cape Town
[edible_berry] => blueberry
[color] => blue
)
Output ($result):
Array
(
[fruit] => apple
[veg] => pumpkin
[berry] => blueberry
)
First: yes, that would work, however I would rewrite it a bit:
foreach ($assoc_array as $key => $value) {
if (strpos($key, 'edible_') === 0) {
$result[] = $value;
}
}
Regarding Second: You are asking how to remove the key from the output array. However you did not even push the key into the output array, you only pushed the value. If you'd like to also push the key, you should do it like this:
$result[$key] = $value;
But since you haven't done that, there's no need to remove the key.
If you however meant removing the edible_ part of the key from the $assoc_array, you'd just need to add a line to the loop and pass the key by reference by adding a &:
foreach ($assoc_array as &$key => $value) {
if (strpos($key, 'edible_') === 0) {
$key = str_replace('edible_', '', $key)
$result[] = $value;
}
}
Edit: As OP told me in comments, she wants to push the key without the edible part. So just do it like this:
foreach ($assoc_array as $key => $value) {
if (strpos($key, 'edible_') === 0) {
$key = str_replace('edible_', '', $key)
$result[$key] = $value;
}
}
This should work for you:
First I remove all elements, which doesn't have edible_ at the start of the key with array_diff_ukey(). After this I simply array_combine() the elements with they keys, where I remove the prefix with array_map() and substr().
<?php
$assoc_array = array('edible_fruit'=>'apple', 'animal'=>'cat', 'edible_veg'=>'pumpkin', 'number'=>'23', 'city'=>'Cape Town', 'edible_berry'=>'blueberry', 'color'=>'blue');
//remove elemnts
$result = array_diff_ukey($assoc_array, ["edible_" => ""], function($k1, $k2){
return substr($k1, 0, 7) == $k2;
});
//change keys
$result = array_combine(
array_map(function($v){
return substr($v, 7);
}, array_keys($result)),
$result);
print_r($result);
?>
output:
Array ( [fruit] => apple [veg] => pumpkin [berry] => blueberry )
You can loop through the array and search if the key has the string that you can eliminate and make your $newArray
<?php
$assoc_array = array('edible_fruit'=>'apple', 'animal'=>'cat', 'edible_veg'=>'pumpkin', 'number'=>'23', 'city'=>'Cape Town', 'edible_berry'=>'blueberry', 'color'=>'blue');
$search = 'edible_';
$newArray = array();
#First and Second Step
foreach($assoc_array as $key => $value) {
if(strpos($key, "edible_") === 0) {
$newArray[substr($key, 7)] = $value;
}
}
print_r($newArray);
echo "<br>\n";
#Third Step
foreach($newArray as $key => $value) {
echo "$key => $value " . date("y/m/d G.i:s", time()) . "<br>\n";
}
Output:
#First and Second Step
Array ( [fruit] => apple [veg] => pumpkin [berry] => blueberry )
#Third Step
fruit => apple 15/04/10 3.02:16
veg => pumpkin 15/04/10 3.02:16
berry => blueberry 15/04/10 3.02:16

How to search by key=>value in a multidimensional array in PHP

Is there any fast way to get all subarrays where a key value pair was found in a multidimensional array? I can't say how deep the array will be.
Simple example array:
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1")
);
When I search for key=name and value="cat 1" the function should return:
array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>3,name=>"cat 1")
);
I guess the function has to be recursive to get down to the deepest level.
Code:
function search($array, $key, $value)
{
$results = array();
if (is_array($array)) {
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
$results = array_merge($results, search($subarray, $key, $value));
}
}
return $results;
}
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1"));
print_r(search($arr, 'name', 'cat 1'));
Output:
Array
(
[0] => Array
(
[id] => 1
[name] => cat 1
)
[1] => Array
(
[id] => 3
[name] => cat 1
)
)
If efficiency is important you could write it so all the recursive calls store their results in the same temporary $results array rather than merging arrays together, like so:
function search($array, $key, $value)
{
$results = array();
search_r($array, $key, $value, $results);
return $results;
}
function search_r($array, $key, $value, &$results)
{
if (!is_array($array)) {
return;
}
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
search_r($subarray, $key, $value, $results);
}
}
The key there is that search_r takes its fourth parameter by reference rather than by value; the ampersand & is crucial.
FYI: If you have an older version of PHP then you have to specify the pass-by-reference part in the call to search_r rather than in its declaration. That is, the last line becomes search_r($subarray, $key, $value, &$results).
How about the SPL version instead? It'll save you some typing:
// I changed your input example to make it harder and
// to show it works at lower depths:
$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
1 => array(array('id'=>3,'name'=>"cat 1")),
2 => array('id'=>2,'name'=>"cat 2")
);
//here's the code:
$arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
foreach ($arrIt as $sub) {
$subArray = $arrIt->getSubIterator();
if ($subArray['name'] === 'cat 1') {
$outputArray[] = iterator_to_array($subArray);
}
}
What's great is that basically the same code will iterate through a directory for you, by using a RecursiveDirectoryIterator instead of a RecursiveArrayIterator. SPL is the roxor.
The only bummer about SPL is that it's badly documented on the web. But several PHP books go into some useful detail, particularly Pro PHP; and you can probably google for more info, too.
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
1 => array("id"=>2,"name"=>"cat 2"),
2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
return ($ar['name'] == 'cat 1');
//return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});
echo "<pre>";
print_r($arr);
?>
Ref: http://php.net/manual/en/function.array-filter.php
Came back to post this update for anyone needing an optimisation tip on these answers, particulary John Kugelman's great answer up above.
His posted function work fine but I had to optimize this scenario for handling a 12 000 row resultset. The function was taking an eternal 8 secs to go through all records, waaaaaay too long.
I simply needed the function to STOP searching and return when match was found. Ie, if searching for a customer_id, we know we only have one in the resultset and once we find the customer_id in
the multidimensional array, we want to return.
Here is the speed-optimised ( and much simplified ) version of this function, for anyone in need. Unlike other version, it can only handle only one depth of array, does not recurse and does away with merging multiple results.
// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {
foreach ($array as $subarray){
if (isset($subarray[$key]) && $subarray[$key] == $value)
return $subarray;
}
}
This brought down the the task to match the 12 000 records to a 1.5 secs. Still very costly but much more reasonable.
if (isset($array[$key]) && $array[$key] == $value)
A minor imporvement to the fast version.
Here is solution:
<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");
$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;
?>
Be careful of linear search algorithms (the above are linear) in multiple dimensional arrays as they have compounded complexity as its depth increases the number of iterations required to traverse the entire array. Eg:
array(
[0] => array ([0] => something, [1] => something_else))
...
[100] => array ([0] => something100, [1] => something_else100))
)
would take at the most 200 iterations to find what you are looking for (if the needle were at [100][1]), with a suitable algorithm.
Linear algorithms in this case perform at O(n) (order total number of elements in entire array), this is poor, a million entries (eg a 1000x100x10 array) would take on average 500,000 iterations to find the needle. Also what would happen if you decided to change the structure of your multidimensional array? And PHP would kick out a recursive algorithm if your depth was more than 100. Computer science can do better:
Where possible, always use objects instead of multiple dimensional arrays:
ArrayObject(
MyObject(something, something_else))
...
MyObject(something100, something_else100))
)
and apply a custom comparator interface and function to sort and find them:
interface Comparable {
public function compareTo(Comparable $o);
}
class MyObject implements Comparable {
public function compareTo(Comparable $o){
...
}
}
function myComp(Comparable $a, Comparable $b){
return $a->compareTo($b);
}
You can use uasort() to utilize a custom comparator, if you're feeling adventurous you should implement your own collections for your objects that can sort and manage them (I always extend ArrayObject to include a search function at the very least).
$arrayObj->uasort("myComp");
Once they are sorted (uasort is O(n log n), which is as good as it gets over arbitrary data), binary search can do the operation in O(log n) time, ie a million entries only takes ~20 iterations to search. As far as I am aware custom comparator binary search is not implemented in PHP (array_search() uses natural ordering which works on object references not their properties), you would have to implement this your self like I do.
This approach is more efficient (there is no longer a depth) and more importantly universal (assuming you enforce comparability using interfaces) since objects define how they are sorted, so you can recycle the code infinitely. Much better =)
$result = array_filter($arr, function ($var) {
$found = false;
array_walk_recursive($var, function ($item, $key) use (&$found) {
$found = $found || $key == "name" && $item == "cat 1";
});
return $found;
});
http://snipplr.com/view/51108/nested-array-search-by-value-or-key/
<?php
//PHP 5.3
function searchNestedArray(array $array, $search, $mode = 'value') {
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
if ($search === ${${"mode"}})
return true;
}
return false;
}
$data = array(
array('abc', 'ddd'),
'ccc',
'bbb',
array('aaa', array('yyy', 'mp' => 555))
);
var_dump(searchNestedArray($data, 555));
function in_multi_array($needle, $key, $haystack)
{
$in_multi_array = false;
if (in_array($needle, $haystack))
{
$in_multi_array = true;
}else
{
foreach( $haystack as $key1 => $val )
{
if(is_array($val))
{
if($this->in_multi_array($needle, $key, $val))
{
$in_multi_array = true;
break;
}
}
}
}
return $in_multi_array;
}
I needed something similar, but to search for multidimensional array by value... I took John example and wrote
function _search_array_by_value($array, $value) {
$results = array();
if (is_array($array)) {
$found = array_search($value,$array);
if ($found) {
$results[] = $found;
}
foreach ($array as $subarray)
$results = array_merge($results, $this->_search_array_by_value($subarray, $value));
}
return $results;
}
I hope it helps somebody :)
This is a revised function from the one that John K. posted... I need to grab only the specific key in the array and nothing above it.
function search_array ( $array, $key, $value )
{
$results = array();
if ( is_array($array) )
{
if ( $array[$key] == $value )
{
$results[] = $array;
} else {
foreach ($array as $subarray)
$results = array_merge( $results, $this->search_array($subarray, $key, $value) );
}
}
return $results;
}
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1"));
print_r(search_array($arr, 'name', 'cat 1'));
function findKey($tab, $key){
foreach($tab as $k => $value){
if($k==$key) return $value;
if(is_array($value)){
$find = findKey($value, $key);
if($find) return $find;
}
}
return null;
}
I think the easiest way is using php array functions if you know your key.
function search_array ( $array, $key, $value )
{
return array_search($value,array_column($array,$key));
}
this return an index that you could find your desired data by this like below:
$arr = array(0 => array('id' => 1, 'name' => "cat 1"),
1 => array('id' => 2, 'name' => "cat 2"),
2 => array('id' => 3, 'name' => "cat 1")
);
echo json_encode($arr[search_array($arr,'name','cat 2')]);
this output will:
{"id":2,"name":"cat 2"}
And another version that returns the key value from the array element in which the value is found (no recursion, optimized for speed):
// if the array is
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);
//then
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) )
// instead of Array ( [0] => Array ( [id] => 2 ) )
// search array for specific key = value
function search_array($array, $key, $value) {
$return = array();
foreach ($array as $k=>$subarray){
if (isset($subarray[$key]) && $subarray[$key] == $value) {
$return[$k] = $subarray;
return $return;
}
}
}
Thanks to all who posted here.
If you want to search for array of keys this is good
function searchKeysInMultiDimensionalArray($array, $keys)
{
$results = array();
if (is_array($array)) {
$resultArray = array_intersect_key($array, array_flip($keys));
if (!empty($resultArray)) {
$results[] = $resultArray;
}
foreach ($array as $subarray) {
$results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
}
}
return $results;
}
Keys will not overwrite because each set of key => values will be in separate array in resulting array.
If you don't want duplicate keys then use this one
function searchKeysInMultiDimensionalArray($array, $keys)
{
$results = array();
if (is_array($array)) {
$resultArray = array_intersect_key($array, array_flip($keys));
if (!empty($resultArray)) {
foreach($resultArray as $key => $single) {
$results[$key] = $single;
}
}
foreach ($array as $subarray) {
$results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
}
}
return $results;
}
2 functions: array_search_key_value which returns the array of keys to reach a key with a value in a multidimensional array, array_extract_keys which returns the value in a multidimensional array pointed to by an array of keys.
function array_search_key_value($array, $key, $value) {
if (!is_array($array)) {
return false;
}
return array_search_key_value_aux($array, $key, $value);
}
function array_search_key_value_aux($array, $key, $value, $path=null) {
if (array_key_exists($key, $array) && $array[$key] === $value) {
$path[]=$key;
return $path;
}
foreach ($array as $k => $v ) {
if (is_array($v)) {
$path[]=$k;
$p = array_search_key_value_aux($v, $key, $value, $path);
if ($p !== false) {
return $p;
}
}
}
return false;
}
function array_extract_keys($array, $key_list) {
$v = $array;
foreach ($key_list as $key) {
if (!is_array($v) || !array_key_exists($key, $v))
return false;
$v = &$v[$key];
}
return $v;
}
Here is a unitary test:
$test_array = array(
'a' => array(
'aa' => true,
'ab' => array(
'aaa' => array(
'one' => 1,
'two' => 2,
'three' => 3,
'four' => 4
),
'four' => 4,
'five' => 5,
),
'six' => 6,
),
'seven' => 7
);
$test_data = array(
array('one', 1),
array('two', 2),
array('three', 3),
array('four', 4),
array('five', 5),
array('six', 6),
array('seven', 7),
array('zero', 0),
array('one', 0),
);
foreach ($test_data as $d) {
$r = array_search_key_value($test_array, $d[0], $d[1]);
echo $d[0] . ' => ' . $d[1] . ' ? ', $r ? implode('/', $r) . ' => ' . array_extract_keys($test_array, $r) : 'null', PHP_EOL;
}

Categories