Get Count of particular entity in array inside array? - php

I have an array named hello as follows,
hello(
0 => ([name => abc, add=>def, city=>ny,phone=>12345]);
1 => ([name => pqr, add=>mno, city=>qw,phone=>67890]);
2 => ([name => abc, add=>def, city=>ny,phone=>14785]);
3 => ([name => ghi, add=>foo, city=>yu,phone=>258]);
4 => ([name => jkl, add=>exy, city=>ny,phone=>95145]);
);
It has few elements and that elements are also array itself, like while retring query result.
I want to count a perticular name that repeats. Like in hello[0] and hello[2], so my ans should be abc=2.
How can I do that?
Thanks.

Edit 2:
Now you want a way to count all of the objects:
$arr = [
0 => (object) ['name' => 'abc', 'add'=>'def', 'city'=>'ny', 'phone'=>'12345'],
1 => (object) ['name' => 'pqr', 'add'=>'mno', 'city'=>'qw', 'phone'=>'67890'],
2 => (object) ['name' => 'abc', 'add'=>'def', 'city'=>'ny', 'phone'=>'14785'],
3 => (object) ['name' => 'ghi', 'add'=>'foo', 'city'=>'yu', 'phone'=>'258'],
4 => (object) ['name' => 'jkl', 'add'=>'exy', 'city'=>'ny', 'phone'=>'95145'],
];
function countBy (callable $f, $xs) {
return array_reduce($xs, function ($acc, $x) use ($f) {
$y = call_user_func($f, $x);
$acc[$y] = isset($acc[$y]) ? $acc[$y] + 1 : 1;
return $acc;
}, []);
}
json_encode(countBy (function ($x) { return $x->name; }, $arr));
// => { "abc": 2, "pqr": 1, "ghi": 1, "jkl": 1 }
Edit:
You lied about the data types so I changed $x['name'] to $x->name and $x[$attr] to $x->{$name}
You can use array_reduce
array_reduce($arr, function ($acc, $x) {
return $x->name === 'abc' ? $acc + 1 : $acc;
}, 0);
// => 2
Or write it as a function
function countAttr($attr, $match, $xs) {
return array_reduce($xs, function ($acc, $x) use ($attr, $match) {
return $x->{$attr} === $match ? $acc + 1 : $acc;
}, 0);
}
countAttr('name', 'abc', $arr)(
// => 2

In this case defining a function would be best so that count for any name cen be get. You can try this -
$array = [
0 => ['name' => 'abc', 'add'=>'def', 'city'=>'ny','phone'=>'12345'],
1 => ['name' => 'pqr', 'add'=>'mno', 'city'=>'qw','phone'=>'67890'],
2 => ['name' => 'abc', 'add'=>'def', 'city'=>'ny','phone'=>'14785'],
3 => ['name' => 'ghi', 'add'=>'foo', 'city'=>'yu','phone'=>'258'],
4 => ['name' => 'jkl', 'add'=>'exy', 'city'=>'ny','phone'=>'95145'],
];
function get_name_count($array, $name)
{
// filter array for name
$temp = array_filter($array, function($a) use($name) {
return ($a['name'] === $name);
});
// return count
return count($temp);
}
var_dump(get_name_count($array, 'abc'));
Output
int(2)

// hello , please use some standard name for array
// code not tested
// define variable if required
$field_name="name";
$value="abc";
$count=0;
echo count_field($field_name,$value);
function count_field($field_name,$value)
{
foreach($hello as $val)
{
if($val[$field_name]==$value)
{
$count++;
}
}
return $count;
}

Related

Filter array with array_walk_recursive but deny specific values

I am trying to filter an array
$array = [
[
'id' => 1,
'some_key' => 'some_value',
'attribute' => [
'id' => 45,
'cat_id' => 1
],
'sub' => [
'id' => 17,
'some_key' => 'some_value',
'attribute' => [
'id' => 47,
'cat_id' => 17
],
],
],
[
'id' => 2,
'some_key' => 'some_value',
'sub' => [
'id' => 19,
'some_key' => 'some_value',
],
]
];
$childArray = [];
array_walk_recursive($array, static function($value, $key) use(&$childArray){
if($key === 'id') {
$childArray[] = $value;
}
});
This returns me an array of all array-fields having id as key.
[1,45,17,47,2,19]
But there is a small problem, some of the array have an key called attribute containing an idkey field that I dont want to have.
[1,17,2,19] //this is what I want
Is there a way to say "don't take the id inside attribute" ?
My current solution, I added a filter before my filter :D
/**
* #param array $array
* #param string $unwanted_key
*/
private function recursive_unset(&$array, $unwanted_key)
{
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
$this->recursive_unset($value, $unwanted_key);
}
}
}
but this seems like this is not the best practice ^^
You can traverse recursively manually instead of array_walk_recursive and avoid all under attribute key.
<?php
$childArray = [];
function collectIDs($arr,&$childArray){
foreach($arr as $key => $value){
if($key === 'attribute') continue;
if(is_array($value)) collectIDs($value,$childArray);
else if($key === 'id') $childArray[] = $value;
}
}
collectIDs($array,$childArray);
print_r($childArray);
Demo: https://3v4l.org/V6uFf
Find a function that will flatten your array. The result should look like this (I have a class for this):
array (
0 =>
array (
'id' => 1,
'some_key' => "some_value",
'attribute.id' => 45,
'attribute.cat_id' => 1,
'sub.id' => 17,
'sub.some_key' => "some_value",
'sub.attribute.id' => 47,
'sub.attribute.cat_id' => 17,
),
1 =>
array (
'id' => 2,
'some_key' => "some_value",
'sub.id' => 19,
'sub.some_key' => "some_value",
),
)
So you have all keys available and can work with a modified array_walk.
$childArray = [];
array_walk_recursive($data, static function($value, $key) use(&$childArray){
$keys = array_reverse(explode(".",$key));
if($keys[0] === 'id' AND (!isset($keys[1]) OR $keys[1] != 'attribute')) {
$childArray[] = $value;
}
});
The RecursiveArrayIterator class is also very suitable when array values ​​are to be collected depending on keys and values ​​on different levels.
$result = [];
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($it as $key => $value) {
$parentLevel = $it->getDepth()-1;
$parentKey = $it->getSubIterator($parentLevel)->key();
if($key === 'id' AND $parentKey !== 'attribute'){
$result[] = $value;
}
}
var_export($result);
//array ( 0 => 1, 1 => 17, 2 => 2, 3 => 19, )
To return the qualifying ids instead of creating a reference variable, merge recursive calls of the function as you iterate.
Code: (Demo)
function getIds($array) {
$ids = [];
foreach ($array as $key => $value) {
if ($key === 'id') {
$ids[] = $value;
} elseif ($key !== 'attribute' && is_array($value)) {
$ids = array_merge($ids, getIds($value));
}
}
return $ids;
}
var_export(getIds($array));
Output:
array (
0 => 1,
1 => 17,
2 => 2,
3 => 19,
)

reducing an array into another array

Inside a laravel blade template, I am trying to reduce an array like this:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
into something like this:
$reducedList = [['count' => 2, 'kg' => 2], ['count' => 1, 'kg' => 3]];
This is what I have so far:
#php
$variableWeights = isset($sale->variable_weight_json) ? collect(json_decode($sale->variable_weight_json, true)) : null;
$groups = array();
if (isset($variableWeights)) {
$groups = $variableWeights->reduce(function($carry, $item) {
$index = array_search($item['kg'], array_column($carry, 'weight'));
if (isset($index)) {
$existing = $carry[$index];
array_splice($carry, $index, 1, [
'count' => $existing['count'] + 1,
'weight' => $item['kg']
]);
} else {
array_push($carry, [
'count' => 1,
'weight' => $item['kg'],
]);
}
return $carry;
}, array());
}
#endphp
But it is giving me the error Undefined offset: 0
I am new to php. How should the code be corrected or is there a better approach to achieve the desired result?
Why dont you reduce it to something simpler like this
$reducedList = [2 => 2, 3 => 1]
where the weight is the index and the value is the count.
$reducedList = [];
foreach ($longList as $box) {
if (isset($reducedList[$box['kg']]) {
$reducedList[$box['kg']]++;
} else {
$reducedList[$box['kg']] = 1;
}
}
This way you avoid complexity but you still get the same amount of information.
I guess you can achieve it with code like:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
$reducedList = array_values(array_reduce(
$longList,
function($carry, $item) {
if (isset($carry[$item['kg']])) {
++$carry[$item['kg']]['count'];
} else {
$carry[$item['kg']] = ['count' => 1, 'kg' => $item['kg']];
}
return $carry;
},
[]
));
print_r($reducedList);
Here is a working example.
don't use isset() function. it checks only variable existing. use empty() or other condition, it will check variable existing and value. try this.
#php
$variableWeights = isset($sale->variable_weight_json) ? collect(json_decode($sale->variable_weight_json, true)) : null;
$groups = array();
if ($variableWeights->isNotEmpty()) {
$groups = $variableWeights->reduce(function($carry, $item) {
$index = array_search($item['kg'], array_column($carry, 'weight'));
if ($index != false ) {
$existing = $carry[$index]?: false;
if ($existing) {
array_splice($carry, $index, 1, [
'count' => $existing['count'] + 1,
'weight' => $item['kg']
]);
}
} else {
array_push($carry, [
'count' => 1,
'weight' => $item['kg'],
]);
}
return $carry;
}, array());
}
#endphp
You can use the sexy countBy() method (combined with the famous map() one) of the Collection class.
Try this:
$longList = [['box' => 1, 'kg' => 2], ['box' => 2, 'kg' => 2], ['box' => 3, 'kg' => 3]];
$shortList = collect($longList)
->countBy('kg')
->map(function ($count, $kg) {
return [
'kg' => $kg,
'count' => $count,
];
});
With that, you'll get this:
dd($shortList);
=> Illuminate\Support\Collection {#3380
all: [
2 => [
"kg" => 2,
"count" => 2,
],
3 => [
"kg" => 3,
"count" => 1,
],
],
}
Here you have a working demo.
isset checks if variable is set (i.e it exists and not NULL). As I (and core developers) can hardly imagine in which cases array_search can return NULL, there's a manual which says:
Returns the key for needle if it is found in the array, FALSE otherwise.
So, you need to check if $index is not false:
$index = array_search($item['kg'], array_column($carry, 'weight'));
// Note that I use `!==` because using `!=` will convert
// `0` index to false, which is not correct in your case
if (false !== $index) {

Convert an array with index and value into an array

I have an array as key => value pair such as:
$array = [ 10 => 'Windows', 12 => 'Keyboard', 15 => 'Monitor' ];
What I would like to achieve without using any foreach or loops the following:
$converted = [
0 => [ 'id' => 10, 'name' => 'Windows'],
1 => [ 'id' => 12, 'name' => 'Keyboard'],
2 => [ 'id' => 15, 'name' => 'Monitor']
];
Here they indices in new array doesn't matter. Any tips??
No foreach and no loop, but now there is a closure:
$result = array_map(function ($id, $name) {
return [
'id' => $id,
'name' => $name
];
}, array_keys($array), array_values($array));
Even if there was a PHP function that did this exactly, it would be using a loop internally.
function do_what_ghazanfar_mir_wants(array $array) {
return array_map(function ($id, $name) {
return [
'id' => $id,
'name' => $name
];
}, array_keys($array), array_values($array));
}
And the single liner is:
$result = do_what_ghazanfar_mir_wants($array);
And the foreach approach for comparison:
$res = [];
foreach ($array as $id => $name) {
$res[] = [ 'id' => $id, 'name' => $name ];
}
If you want to keep it really short then array_walk will do it in one line:
array_walk($array, function(&$value, $key) { $value = ['id' => $key, 'name' => $value]; });
See https://3v4l.org/OEohi
But I think a foreach loop is probably going to be a lot more readable.
Do it with array_map(), Just pass the keys array_keys($array) and values $array as the parameter of your array_map()
<?php
$array = [ 10 => 'Windows', 12 => 'Keyboard', 15 => 'Monitor' ];
function map_by_key($m, $n)
{
return(array('id' => $m, 'name'=>$n));
}
$output = array_map("map_by_key", array_keys($array),$array);
print '<pre>';
print_r($output);
print '</pre>';
?>
DEMO: https://3v4l.org/iTVSm

PHP how to convert an single array to multidimentional array?

I have that single array and I need to convert in a multidimensional array without using array_merge, array_replace_recurcive etc, just an autonomous function:
$single = [
0 => 'one',
1 => 'two',
2 => 'tree',
3 => 'four',
4 => 'five'
];
And convert to look like this, with the last key as value:
$multidimentional = [
'one' => [
'two' => [
'tree' => [
'four' => 'five'
]
]
]
];
I have create a recursion function if this helps:
function array_replace_recursive($defaults, $replaces) {
if(is_null($replaces)) {
$replaces = [];
}
if(!is_array($defaults) || !is_array($replaces)) {
return $replaces;
}
foreach($defaults as $key => $value) {
if(!array_key_exists($key, $replaces) || is_null($replaces[$key])) {
$replaces[$key] = $value;
} else {
if(is_array($replaces[$key]) && is_array($value)) {
$replaces[$key] = array_replace_recursive($replaces[$key], $value);
}
}
}
return $replaces;
}
You can do this with PHP (as of 5.6) argument unpacking and a very simple recursive function:
$single = [
0 => 'one',
1 => 'two',
2 => 'tree',
3 => 'four',
4 => 'five'
];
function convert($key, ...$values) {
if (count($values) == 1)
return array($key => $values[0]);
else
return array($key => convert(...$values));
}
print_r(convert(...$single));
Output:
Array
(
[one] => Array
(
[two] => Array
(
[tree] => Array
(
[four] => five
)
)
)
)
You can also do it without using count (only isset):
function convert2($key, ...$values) {
if (!isset($values[1]))
return array($key => $values[0]);
else
return array($key => convert(...$values));
}
print_r(convert2(...$single));
Output:
Array
(
[one] => Array
(
[two] => Array
(
[tree] => Array
(
[four] => five
)
)
)
)
Thinking in recursion, you can write a base case that returns the value of the currently seen item if it is one less than the length of the array.
$singleDim = [
0 => 'one',
1 => 'two',
2 => 'tree',
3 => 'four',
4 => 'five'
];
function toMultiDimArray($arr, $seen=0) {
if ([] === $arr) {
return [];
}
if(count($arr) - 1 === $seen) {
return $arr[$seen];
}
return [
$arr[$seen] => toMultiDimArray($arr, $seen+1)
];
}
$multiDim = toMultiDimArray($singleDim);
var_dump($multiDim);
array(1) {
["one"]=>
array(1) {
["two"]=>
array(1) {
["tree"]=>
array(1) {
["four"]=>
string(4) "five"
}
}
}
}
$single = [
0 => 'one',
1 => 'two',
2 => 'tree',
3 => 'four',
4 => 'five'
];
function to_multidimentional($array, $count = 0, $seen = 0) {
if($count - 1 === $seen) {
return $array[$seen];
}
return [
$array[$seen] => to_multidimentional($array, $count, $seen + 1)
];
}
$single = to_multidimentional($single, count($single));
print_r($single);
exit;
There's nothing recursive about this. It's just iterating over the $single array backwards and then embedding the result inside the new key each time:
function foo( $single ) {
$multidimensional = [ $single[ count($single) - 1 ] ];
for( $i = count($single) - 2; $i >= 0; $i-- ) {
$multidimensional = [ $single[$i] => $multidimensional ];
}
}

PHP : multidimensional array merge recursive

I need to merge those two arrays:
$ar1 = array("color" => array("red", "green"), "aa");
$ar2 = array("color" => array( "green", "blue"), "bb");
$result = array_merge_recursive($ar1, $ar2);
Expected output:
[
'color' => [
(int) 0 => 'red',
(int) 1 => 'green',
(int) 3 => 'blue'
],
(int) 0 => 'aa',
(int) 1 => 'bb'
]
But it outputs:
[
'color' => [
(int) 0 => 'red',
(int) 1 => 'green',
(int) 2 => 'green', (!)
(int) 3 => 'blue'
],
(int) 0 => 'aa',
(int) 1 => 'bb'
]
I'm looking for the simplest way to do this, my array inputs won't be deeper than those examples.
Here it is.
function array_merge_recursive_ex(array $array1, array $array2)
{
$merged = $array1;
foreach ($array2 as $key => & $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = array_merge_recursive_ex($merged[$key], $value);
} else if (is_numeric($key)) {
if (!in_array($value, $merged)) {
$merged[] = $value;
}
} else {
$merged[$key] = $value;
}
}
return $merged;
}
Thanks to Meglio comment, you can use these functions for any number of arrays :
Functions
function drupal_array_merge_deep() {
$args = func_get_args();
return drupal_array_merge_deep_array($args);
}
// source : https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_array_merge_deep_array/7.x
function drupal_array_merge_deep_array($arrays) {
$result = array();
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
// Renumber integer keys as array_merge_recursive() does. Note that PHP
// automatically converts array keys that are integer strings (e.g., '1')
// to integers.
if (is_integer($key)) {
$result[] = $value;
}
elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
$result[$key] = drupal_array_merge_deep_array(array(
$result[$key],
$value,
));
}
else {
$result[$key] = $value;
}
}
}
return $result;
}
Usage
$merged = drupal_array_merge_deep($ar_1, $ar_2);
var_dump($merged);
$merged = drupal_array_merge_deep_array([$ar_1, $ar_2]);
var_dump($merged);
Usage (test data)
$ar_1 = [
"item1" => false,
"item2" => true,
"item_list" => [
"sub_item1" => 5,
"sub_itemlist" => [
"sub_sub_item1" => 27,
],
]
];
$ar_2 = [
"item_list" => [
"sub_item2" => 5,
"sub_itemlist" => [
"sub_sub_item2" => 27,
],
],
"item3" => true,
];
Usage output (same for both functions)
array (size=4)
'item1' => boolean false
'item2' => boolean true
'item_list' =>
array (size=3)
'sub_item1' => int 5
'sub_itemlist' =>
array (size=2)
'sub_sub_item1' => int 27
'sub_sub_item2' => int 27
'sub_item2' => int 5
'item3' => boolean true

Categories