I built a function to search only a specific key in a multidimensional array (not to be confused with in_array and array_search which searches every element. I am trying to return the key of the sub-array in the multidimensional array in which has a match.
$array = array(array("hello1", "hello2"), array("test1", "test2"));
function search_custom($needle, $specific_key) {
global $array;
foreach($array as $value) {
/* only searches specific key in the sub-arrays */
if($needle == $value[$specific_key]) {
return key($value); /* should return 1? */
}
}
}
print_r(search_custom("test2", 1)); /* search only in element 1 of sub-arrays */
This unfortunately outputs "0," even though "test2" is in element 1 of the multi-array.
Your use of key() is wrong, it expects an array. But the foreach can give you the key, you don't have to look for it:
$array = array(array("hello1", "hello2"), array("test1", "test2"));
function search_custom($needle, $specific_key) {
global $array;
foreach($array as $key=>$value) {
/* only searches specific key in the sub-arrays */
if($needle == $value[$specific_key]) {
return $key;
}
}
}
print_r(search_custom("test2", 1)); /* search only in element 1 of sub-arrays */
Related
Is there no built in function for this? I was only able to find
array_key_first and last;
I made this function , that sort of works, but still...
/**
* Get array key from position
*/
function get_array_key($arr, $pos = 0)
{
foreach ($arr as $key => $value) {
$mykey[] = $key;
}
if (isset($mykey[$pos])) return $mykey[$pos];
}
You can just use array_keys:
echo array_keys($arr)[$pos];
Here's a demo comparing it to your function.
I have an array which consists of arrays. So, now suppose I want to retrieve the sku and price whose
key value is 2=>5 and 3=>7 so it should return price=>13 and sku=>bc i.e. that array whose index is at 1 in the array.
Hi I would probably try the following (Same as Riziers comment)
foreach($array as $key => $item) {
if($item[2] == 5 && $item[3] == 7) {
// return price
return $item;
}
}
There is a function array_search, which does what you want but for simple values. You can define your own function that will take not $needle, but callable predicate:
function array_search_callback(callable $predicate, array $array)
{
foreach ($array as $key => $item) {
if ($predicate($item)) {
return $key;
}
}
return false;
}
Having this function your example can be done like this:
$key = array_search_callback(function ($item) {
return $item[2] === '5' && $item[3] === '7';
}, $array);
$result = $key === false ? null : $array[$key];
I could simply return an item from the search function. But to be consistent with the original search function, I am returning the index.
As array_search_callback takes callable as an argument you can provide any criteria you want without the need of modifying the function itself.
Here is working demo.
This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
So I have this (simple) method:
/**
* #param $needle
* #param $haystack
*
* #return array
*/
public function recursiveArraySearch($needle, $haystack)
{
$array = false;
foreach ($haystack as $key => $value) {
if ($key === $needle) {
$array = $value;
} elseif (is_array($value)) {
$this->recursiveArraySearch($needle, $value);
}
}
return $array;
}
Which is called like so:
$result = $this->recursiveArraySearch('some_index', $configArray);
It am having trouble return it once and for all back to $result`.
If the $needle matches the $key then I just want it to return the value but at the moment it's returning to itself.
Something I haven't actually done yet.
Thanks
UPDATE: When I return the method as the answers suggest and it reached the end of an array node (like a binary tree search) it passes a string in as the $haystack and thus return false.
Data Structure:
I may want to get the values of key circled red or I may want the values of the key circled orange?
The function needs to return them of false.
you can do this
public function recursiveArraySearch($needle, $haystack)
{
foreach ($haystack as $key => $value) {
if ($key === $needle) {
return $value;
} elseif (is_array($value)) {
$check = $this->recursiveArraySearch($needle, $value);
if($check)
return $check;
}
}
return false;
}
public function recursiveArraySearch($needle, $haystack)
{
foreach ($haystack as $key => $value) {
if ($key === $needle) {
return $value;
} elseif (is_array($value)) {
$result = $this->recursiveArraySearch($needle, $value);
if ($result !== false){
return $result;
}
}
}
return false;
}
When you recurse down you need to check the result and return only if an item was found. If nothing was found then you need to let the loop continue.
This assumes that your array does not contain any boolean values. If it does, you'll need to use an alternate method to avoid confusing a false value for not found.
I edited this answer to fit your needs.
function findKey($array, $keySearch)
{
foreach ($array as $key => $item) {
if ($key == $keySearch) {
return $item;
}
else {
if (is_array($item)) {
$keyFound = findKey($item, $keySearch);
if( $keyFound != false ) {
return $keyFound;
}
}
}
}
return false;
}
A number of problems here. First and foremost you are not assigning the data returned from the recursive call to any kind of data structure. Also, you should be doing a better job of checking edge conditions. Finally, if your Doc Block says that array is returned, you need to 100% make sure you are returning an array. That is the contract you are making with the caller when they read the documentation on this method, so you should adhere to that.
The example below assumes you are just going to return a numerically indexed array of values to the initial caller. This example includes a merge of recursive results to active array, better handling around input validation, and the consistent return of a numerically-indexed array (with empty array signifying no results).
/**
* #param mixed $needle Integer or string key value used for recursive search.
* #param array $haystack Array to be searched.
*
* #throws InvalidArgumentException
*
* #return array Return numerically-indexed array with empty array if no match.
*/
public function recursiveArraySearch($needle, array $haystack)
{
// validate that we have a proper needle passed
if(!is_int($needle) && !is_string($needle)) {
throw new InvalidArgumentException(
'Invalid search needle type passed as argument. ' .
"Integer or string value expected. Value passed:\n" .
var_export($needle, true)
);
}
$array = [];
foreach ($haystack as $key => $value) {
// recursively search if $value is non-empty array
if(is_array($value) && !empty($value)) {
array_merge($array, $this->recursiveArraySearch($needle, $value));
}
// otherwise, we can make exact string/integer comparison
else if ($key === $needle) {
$array[] = $value;
}
}
return $array;
}
Note here that I am assuming you are looking for all matches in the recursive structure. If you are looking for the first match, you can do something like the following, which is a breadth-first search.
/**
* #param mixed $needle Integer or string key value used for recursive search.
* #param array $haystack Array to be searched.
*
* #throws InvalidArgumentException
*
* #return mixed Return values could be mixed since we have no constraint on
* value types in haystack. Null will be returned on no match, thus
* this function cannot differentiate explicitly defined null values
* from no match.
*/
public function recursiveBreadthFirstSingleMatchArraySearch($needle, array $haystack)
{
// validate that we have a proper needle passed
if(!is_int($needle) && !is_string($needle)) {
throw new InvalidArgumentException(
'Invalid search needle type passed as argument. ' .
"Integer or string value expected. Value passed:\n" .
var_export($needle, true)
);
}
// see if there is key match at first level of array
if(array_key_exists($needle, $haystack)) {
return $haystack[$needle];
}
// iterate through haystack performing recursive search on array until match
foreach ($haystack as $key => $value) {
// recursively search if $value is non-empty array
if(is_array($value) && !empty($value)) {
$result = $this->
recursiveBreadthFirstSingleMatchArraySearch($needle, $value));
if (!is_null($result)) {
return $result;
}
}
}
return null;
}
I have a function that searches a multidimensional array for a key, and returns the path
inside the array to my desired key as a string.
Is there any way I can use this string in php to reach this place in my original array, not to get to the value but to make changes to this specific bracnch of the array?
An example:
$array = array('first_level'=>array(
'second_level'=>array(
'desired_key'=>'value')));
in this example the function will return the string:
'first_level=>second_level=>desired_key'
Is there a way to use this output, or format it differently in order to use it in the following or a similar way?
$res = find_deep_key($array,'needle');
$array[$res]['newkey'] = 'injected value';
Thanks
If the keys path is safe (e.g. not given by the user), you can use eval and do something like:
$k = 'first_level=>second_level=>desired_key';
$k = explode('=>', $k);
$keys = '[\'' . implode('\'][\'', $k) . '\']';
eval('$key = &$array' . $keys . ';');
var_dump($key);
I think you want to do a recursive search in the array for your key? Correct me if i am wrong.
Try this
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
Taken from here http://in3.php.net/manual/en/function.array-search.php#91365
You need something like:
find_key_in_array($key, $array, function($foundValue){
// do stuff here with found value, e.g.
return $foundValue * 2;
});
and the implementation would be something like:
function find_key_in_array($key, $array, $callback){
// iterate over array fields recursively till you find desired field, then:
...
$array[$key] = $callback($array[$key]);
}
If you need to append some new sub-array into multidimensional complex array and you know where exactly it should be appended (you have path as a string), this might work (another approach without eval()):
function append_to_subarray_by_path($newkey, $newvalue, $path, $pathDelimiter, &$array) {
$destinationArray = &$array;
foreach (explode($pathDelimiter, $path) as $key) {
if (isset($destinationArray[$key])) {
$destinationArray = &$destinationArray[$key];
} else {
$destinationArray[$newkey] = $newvalue;
}
}
}
$res = find_deep_key($array,'needle');
append_to_subarray_by_path('newkey', 'injected value', $res, '=>', $array);
Of course, it will work only if all keys in path already exist. Otherwise it will append new sub-array into wrong place.
just write a function that takes the string and the array. The function will take the key for each array level and then returns the found object.
such as:
void object FindArray(Array[] array,String key)
{
if(key.Length == 0) return array;
var currentKey = key.Split('=>')[0];
return FindArray(array[currentKey], key.Remove(currentKey));
}
I need to do fast lookups to find if an array exists in an array. If I knew the depth of the array It would be easy - and fast!
$heystack['lev1']['lev2']['lev3'] = 10; // $heystack stores 10,000s of arrays like this
if(isset($heystack[$var1][$var2][$var3])) do something...
How would you do this dynamically if you don't know the depth? looping and searching at each level will be too slow for my application.
Your question has already the answer:
if (isset($heystack[$var1][$var2][$var3]))
{
# do something...
}
If you don't know the how many $var1 ... $varN you have, you can only do it dynamically which involves either looping or eval and depends if you need to deal with string or numerical keys. This has been already asked and answered:
Loop and Eval: use strings to access (potentially large) multidimensional arrays (and that's only one of the many)
If you are concerned about speed, e.g. if the array is always the same but you need to query it often, create a index first that has compound keys so you can more easily query it. That could be either done by storing all keys while traversing the array recursively:
class CompoundKeys extends RecursiveIteratorIterator
{
private $keys;
private $separator;
public function __construct($separator, RecursiveIterator $iterator, $mode = RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
{
$this->separator = $separator;
parent::__construct($iterator, $mode, $flags);
}
public function current()
{
$current = parent::current();
if (is_array($current))
{
$current = array_keys($current);
}
return $current;
}
public function key()
{
$depth = $this->getDepth();
$this->keys[$depth] = parent::key();
return implode('.', array_slice($this->keys, 0, $depth+1));
}
}
Usage:
$it = new CompoundKeys('.', new RecursiveArrayIterator($array));
$compound = iterator_to_array($it, 1);
isset($compound["$var1.$var2.$var3"]);
Alternatively this can be done by traversing recursively and referencing the original arrays values:
/**
* create an array of compound array keys aliasing the non-array values
* of the original array.
*
* #param string $separator
* #param array $array
* #return array
*/
function array_compound_key_alias(array &$array, $separator = '.')
{
$index = array();
foreach($array as $key => &$value)
{
if (is_string($key) && FALSE !== strpos($key, $separator))
{
throw new InvalidArgumentException(sprintf('Array contains key ("%s") with separator ("%s").', $key, $separator));
}
if (is_array($value))
{
$subindex = array_compound_key_alias($value, $separator);
foreach($subindex as $subkey => &$subvalue)
{
$index[$key.$separator.$subkey] = &$subvalue;
}
}
else
{
$index[$key] = &$value;
}
}
return $index;
}
Usage:
$index = array_compound_key_alias($array);
isset($index["$var1.$var2.$var3"]);
You'll need some sort of looping but you won't need to traverse the entire depth. You can simply use a function that does the equivalent of $heystack[$var1][$var2][$var3], but dynamically:
$heystack['lev1']['lev2']['lev3'] = 10;
echo getElement($heystack, array('lev1', 'lev2', 'lev3')); // you could build second parameter dynamically
function getElement($array, $indexes = array())
{
foreach ($indexes as $index) {
$array = $array[$index];
}
return $array;
}
// output: 10
You'll need to put in some defense mechanisms to make the function more robust (for elements/indexes that don't exist) but this is the basic approach.