Get keys from multidimentional array by value - php

I need to get all "top level" keys from multidimensional array by searching the "bottom level" values. Here is an example of the array:
$list = array (
'person1' => array(
'personal_id' => '1',
'short_information' => 'string',
'books_on_hand' => array(
'Book 1',
'Book 2',
'Book 3',
)
),
'person2' => array(
'personal_id' => '2',
'short_information' => 'string',
'books_on_hand' => array(
'Book 4',
'Book 2',
'Book 5',
)
),
'person3' => array(
'personal_id' => '3',
'short_information' => 'string',
'books_on_hand' => array(
'Book 4',
'Book 2',
'Book 1',
'Book 3',
)
),
//etc...
);
I want to know all persons who have "Book 2" on hand. I can get that information by loop like this:
foreach ($list as $person => $info){
$check = array_search( 'Book 2', array_column($info, 'books_on_hand') );
if ( $check !== false ){
$results .= 'Name: '.$person;
$results .= 'ID: '.$info['personal_id'];
//get other infos other stuff, if necessary
}
}
The problem is, that foreach in this case is very heavy on memory and only grows more when array has a thousand+ entries. It needs to run through all of the persons, even if only 3 persons at the very top of the array have "Book 2".
I have been trying to optimize it by getting persons with "Book 2" using built-in functions like array_search, array_keys, array_column and only then run foreach for persons found, but I had no luck with getting "top level" keys.
Is it possible to optimize or use built-in function to search multidimensional array?

One way would be to filter it first. Now your result is structured like $list but it only contains elements with the needed book:
$find = 'Book 2';
$result = array_filter($list, function($v) use($find) {
return in_array($find, $v['books_on_hand']);
});
If all you're interested in is the person key and personal_id then this:
$find = 'Book 2';
$result = array_map(function($v) use($find) {
if(in_array($find, $v['books_on_hand'])) {
return $v['personal_id'];
}
}, $list);
Will return something like this for persons with the needed book:
Array
(
[person1] => 1
[person2] => 2
[person3] => 3
)

Related

How to search an array of associative arrays with multiple required needles and return the indexes?

I am trying to search an array of associative arrays and get the keys of the subarrays that meet all search criteria.
Here is my example:
$list = [
['name' => 'this is items name',
'number' => 1,
'description' => 'this is description',
'id' => 'just some id',],
['name' => 'this is items name2',
'number' => 1,
'description' => 'this is description2',
'id' => 'just some id',],
['name' => 'this is items name3',
'number' => 1,
'description' => 'this is',
'id' => 'just some id',],
];
I would like to search for "this description" and get the keys of an array like this:
Array ( [0] => 0 [1] => 1 )
I tried this:
$array_key = array_keys(array_column($list, 'description'), 'this description', false);
but it only finds the key if search words exactly match the value. How can I solve this problem?
More precisely, how can I search the whole array not only the column description?
You can use this three cases as starting point:
$list=[
['name' => 'this is items name',
'number' => 1,
'description' => 'this is description',
'id' => 'just some id',],
['name' => 'this is items name2',
'number' => 1,
'description' => 'this is description2',
'id' => 'just some id',],
['name' => 'this is items name3',
'number' => 1,
'description' => 'this is',
'id' => 'just some id',],
];
$search='this description';
//first type of search : any of the word containing in the needle sentence
$regex='#'.implode('|',preg_split('#\s#',$search)).'#';
$array_key = array_keys(preg_grep($regex,array_column($list, 'description')));
print_r($array_key);
//second type of search : all of the words containing in the needle sentence
$array_key=[];
$needles=preg_split('#\s#',$search);
$count_needles=count($needles);
$regex='#'.implode('|',$needles).'#';
foreach(array_column($list,'description') as $k=>$v){
if(preg_replace($regex,'###',$v,-1,$count)){
if($count===$count_needles) $array_key[]=$k;
}
}
print_r($array_key);
//third type of search : based on similarity percentage
$thereshold=50.0;
$array_key=[];
$needles=preg_split('#\s#',$search);
$count_needles=count($needles);
$regex='#'.implode('|',$needles).'#';
foreach(array_column($list,'description') as $k=>$v){
if(preg_replace($regex,'###',$v,-1,$count)){
if(min($count,$count_needles)/max($count,$count_needles)*100>$thereshold) $array_key[]=$k;
}
}
print_r($array_key);
output:
Array
(
[0] => 0
[1] => 1
[2] => 2
)
Array
(
[0] => 0
[1] => 1
)
Array
(
[0] => 0
[1] => 1
)
I'm not really sure what you want, but I guess this might be a good starting point:
<?php
$list = [
[
'name' => 'this is items name',
'number' => 1,
'description' => 'this is description',
'id' => 'just some id'
],
[
'name' => 'this is items name2',
'number' => 1,
'description' => 'this is description2',
'id' => 'just some id'
],
'test' => [
'name' =>
'this is items name3',
'number' => 1,
'description' => 'do not find this one',
'id' => 'just some id'
],
'preserve_this_key_please' => [
'name' =>
'this is items name4',
'number' => 1,
'description' => 'this is something, key preserved',
'id' => 'just some id'
],
];
function getKeysBy($array, $key, $value){
return array_keys(array_filter($array,function($val) use ($key,$value) {
//return $val[$key] === $value;//exact match
return strpos($val[$key], $value) === 0;
} ));
}
var_dump(getKeysBy($list, 'description', 'this is'));
Output:
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
string(24) "preserve_this_key_please"
}
Working code:
https://3v4l.org/Gs3He
Note: in this example the keys are preserved, i.e. if a key is a string like in the last item. If you don't need it, then the array_column + friends is enough.
You might use a foreach and preg_match with a regex to match from this until the first occurence of description by matching any character one or more times non greedy .*?:
\bthis.*?description
$result = [];
foreach ($list as $key => $value) {
if (preg_match('/\bthis.+?description/', $value["description"])) {
$result[$key] = $key;
}
}
print_r($result);
Demo
I'm interpretting your question as: How to search a multidimensional array by searching for multiple needle strings -- all of which are required -- among all of the values in a given subarray and return the index of each qualifying subarray.
My solution seeks direct results by condensing the subarray data into a single string to be checked in an iterative fashion. As soon as a subarray is deemed "disqualified", the loop is directed to process the next subarray for highest efficiency.
*note, if you specifically need to make "whole word" matching then regex with word boundaries will be necessary.
If you are not familiar with what continue does, here is a link to the manual.
Code: (Demo)
$qualifiers = [];
$needles = explode(" ", "this description");
foreach ($list as $index => $set) {
$smashed = implode($set);
foreach ($needles as $needle) {
// echo "\n$smashed";
if (strpos($smashed, $needle) === false) {
continue 2;
}
}
$qualifiers[] = $index;
}
var_export($qualifiers);
Output:
array (
0 => 0,
1 => 1,
)
p.s. if you are processing a resultset from a database query, it would be better / more direct to perform your filtering task within the query because then you would avoid generating a resultset which is bloated with data that you don't want.

Counting items in each category of a multidimensional array

I have a multidimensional array and I need to count how many items are in each category:
array (
array(
'name' => 'Bob',
'category' => '2'
),
array(
'name' => 'Bill',
'category' => '6'
),
array(
'name' => 'John',
'category' => '1'
),
array(
'name' => 'Jack',
'category' => '2'
),
)
I want to be able to split these up into categories.
For example;
Category 2 contains 2 items
Category 1 contains 1 item
Category 6 contains 1 item
Just to get the count of each category would be great, but to be able to re-arrange the array into categories would also be useful. I'd like to be able to do both.
I've tried searching StackOverflow but I couldn't find this specific query. I'm guessing this may use array_map somewhere but I'm not good with that function.
Any help is greatly appreciated!
If your array isn't too big a straightforward approach might be the easiest one. Create a new array, use categories as keys and iterate over your array, counting items.
I have written 3 functions that solves the criteria you have described. Keep in mind these functions are bare minimum and lack error handling. It is also assumed the $categories array which all the functions requires has the structure outlined in your question.
The first rearranges all items into the correct category.
function rearrangeCategories(array $categories) {
$calculated = [];
foreach($categories as $category) {
$calculated[$category['category']][] = $category['name'];
}
return $calculated;
}
The second creates an associative array of the amount of items in each category. The array index is the category name/id and the value is an integer declaring the amount of items.
function categoriesCount(array $categories) {
$calculated = [];
$arranged = rearrangeCategories($categories);
foreach($arranged as $category => $values) {
$calculated[$category] = count($values);
}
return $calculated;
}
The third function checks how many items are stored inside a specific category. If the category doesn't exists FALSE is returned. Otherwise an integer is returned.
function categoriesItemCount(array $categories, $key) {
$arranged = rearrangeCategories($categories);
if(!array_key_exists($key, $arranged)) {
return false;
}
return count($arranged[$key]);
}
I hope this helps, happy coding.
You can use something like this
$arr =
array (
array(
'name' => 'Bob',
'category' => '2'
),
array(
'name' => 'Bill',
'category' => '6'
),
array(
'name' => 'John',
'category' => '1'
),
array(
'name' => 'Jack',
'category' => '2'
),
);
$categoryCount = array();
$categoryList = array();
array_map(function($a) use (&$categoryCount, &$categoryList) {
$categoryId = $a['category'];
if (!isset($categoryCount[$categoryId])) {
$categoryCount[$categoryId] = 0;
}
$categoryCount[$categoryId]++;
if (!isset($categoryList[$categoryId])) {
$categoryList[$categoryId] = array();
}
$categoryList[$categoryId][] = $a['name'];
}, $arr);
print_r($categoryCount);
print_r($categoryList);
This will create 2 arrays: one with the counts and one with the elements rearranged
Try this way, i think it will fulfill your requirements.
$arr=array (
array(
'name' => 'Bob',
'category' => '2'
),
array(
'name' => 'Bill',
'category' => '6'
),
array(
'name' => 'John',
'category' => '1'
),
array(
'name' => 'Jack',
'category' => '2'
),
);
$result = call_user_func_array('array_merge_recursive', $arr);
//for just show array
print '<pre>';
print_r(array_count_values($result['category']));
print '</pre>';
//loop as you need
foreach(array_count_values($result['category']) as $k=>$v){
$item=($v>1)? 'items':'item';
echo "Category ".$k." Contains " .$v." ".$item."<br/>";
}

matching multidimensional arrays by key value, preserving other values

I have an array that contains data in the following format:
$myArray = array(
array(
'id' = 1,
'title' = 'the first entry',
'data' = 'additional data'
),
array(
'id' = 2,
'title' = 'the third entry',
'data' = 'some more data'
),
array(
'id' = 3,
'title' = 'the second entry',
'data' = 'other important stuff'
)
);
(An array containing about 12 child arrays in total). This data needs to be split into two rows for displaying on my page, based on the title attribute. I know what titles I want on the first row, and which ones for the second row. So I have another array like this:
$firstRow = array('the first entry', 'the second entry');
$secondRow = array('the third entry');
So what I need to do is throw these 3 arrays, $myArray, $firstRow, $secondRow into a function which will output a new ordered array which preserves the other attributes (the id and data keys in my example) like the following:
$newArray = array(
'firstRow' => array(
array(
'id' = 1,
'title' = 'the first entry',
'data' = 'additional data'
),
array(
'id' = 2,
'title' = 'the second entry',
'data' = 'some more data'
)
),
'secondRow' => array(
array(
'id' = 3,
'title' = 'the third entry',
'data' = 'other important stuff'
)
)
);
I have some ideas, and I know there are various functions like array_intersect(), but I'm not sure which is the best to use? Hopefully someone has a quick and easy solution to this. Thanks.
Use a foreach to loop over the titles array, and create a new array inside the loop, depending on the title value:
function groupTitlesintoArrays($myArray, $firstRow, $secondRow) {
$result = array();
foreach ($myArray as $innerArray) {
foreach ($firstRow as $row) {
if ($row == $innerArray['title']) {
$result['firstRow'][] = $innerArray;
}
}
foreach ($secondRow as $row) {
if ($row == $innerArray['title']) {
$result['secondRow'][] = $innerArray;
}
}
}
return $result;
}
Demo

Multidimensional Array - Pulling Data

New to PHP and after spending hours researching on here, nothing seems to be exactly what I need. I have a multi dimensional array that I'm looking to pull data and COUNT from. FOR Instance:
array (
'loyola' => NULL,
'gold_coast' => NULL,
'lincolnpark' =>
array (
0 => 'Building 1',
1 => 'Building 2',
2 => 'Building 3',
3 => 'Building 4'
),
'lakeview' =>
array (
0 => 'Building 1',
1 => 'Building 2',
2 => 'Building 3'
),
)
I'm looking to essentially create a table that lists all the buildings and in the next column the number of times that building appears.
This what I've gotten thus far, but it only displays all buildings.
$buildings = unserialize($row['buildings']);
$lincolnpark = $buildings['lincolnpark'];
$loyola= $buildings['loyola'];
$gold_coast = $buildings['gold_coast'];
$lakeview = $buildings['lakeview'];
foreach ($lakeview as $value)
{
echo $value;
}
}
Try the code below. It will navigate recursively into the array and will print the qtd each build appear.
<?php
$arr = array (
'loyola' => NULL,
'gold_coast' => NULL,
'lincolnpark' =>
array (
0 => 'Building 1',
1 => 'Building 2',
2 => 'Building 3',
3 => 'Building 4'
),
'lakeview' =>
array (
0 => 'Building 1',
1 => 'Building 2',
2 => 'Building 3'
),
);
$ret = array();
countBuildings($arr);
foreach($ret as $key=>$value){
echo "Building: $key ==> qtd : $value <br>";
}
function countBuildings($arr = array()){
global $ret;
foreach($arr as $value){
if(is_array($value)){
countBuildings($value);
}else{
if($value != NULL){
if(isset($ret[$value])){
$ret[$value] += 1;
}else{
$ret[$value] = 1;
}
}
}
}
}
Do it in two passes: one for counting building occurrences in a separate array, and another for output.

PHP How can I pass values from one array to another?

I'm trying to pass some of the values from theOptions array and drop them into a new array called $theDefaults.
$theOptions = array(
'item1' => array('title'=>'Title 1','attribute'=>'Attribute 1','thing'=>'Thing 1'),
'item2' => array('title'=>'Title 2','attribute'=>'Attribute 2','thing'=>'Thing 2'),
'item3' => array('title'=>'Title 3','attribute'=>'Attribute 3','thing'=>'Thing 3')
);
So, $theDefaults array should look like this:
$theDefaults = array(
'Title 1' => 'Attribute 1',
'Title 2' => 'Attribute 2',
'Title 3' => 'Attribute 3'
);
However, I cannot figure out how to do this.
Have tried this but it is clearly not quite working.
$theDefaults = array();
foreach($theOptions as $k=>$v) {
array_push($theDefaults, $v['title'], $v['attribute']);
}
but when I run this...
foreach($theDefaults as $k=>$v) {
echo $k .' :'.$v;
}
It returns this.
0 :Title 11 :Attribute 12 :Title 23 :Attribute 24 :Title 35 :Attribute 3
Looks to be soooo close, but why are the numbers in the array?
It's even simpler than that:
$theDefaults = array();
foreach($theOptions as $v) {
$theDefaults[$v['title']] = $v['attribute'];
}

Categories