How to count all values in a multidimensional array? - php

I have tried solutions to many similar questions, but they all seem to give me a count for each array. So I have the following array:
Array
(
[1] => Array
(
[0] => 1
[1] => 12
[2] => 2
)
[2] => Array
(
[0] => 1
[1] => 13
[2] => 3
)
[3] => Array
(
[0] => 1
[1] => 12
[2] => 2
)
[4] => Array
(
[0] => 1
)
[5] => Array
(
[0] => 1
)
)
I am trying to count the duplicates across all arrays. So the output should show:
Five 1's
Two 12's
One 13
Two 2's
At the moment I am trying:
foreach($data as $key => $row) {
print_r(array_count_values($row));
}
Which outputs the counts for each individual array
Array
(
[1] => 1
[12] => 1
[2] => 1
)
Array
(
[1] => 1
[13] => 1
[3] => 1
)
Array
(
[1] => 1
[12] => 1
[2] => 1
)
Array
(
[1] => 1
)
Array
(
[1] => 1
)
I have also tried this:
foreach ($data as $key => $row) {
$counts = array_count_values(array_column($data, $key));
var_dump($counts);
}
Which seems to miss a lot of information, like the count of the 1's
array(2) {
[12]=>
int(2)
[13]=>
int(1)
}
array(2) {
[2]=>
int(2)
[3]=>
int(1)
}
array(0) {
}
array(0) {
}
array(0) {
}
As a note, the initial array keys will not always be sequential, as this represents a row number. So this array may contain rows 1, 2, 5, 6, 7 etc.
How would I go about counting all duplicates together?

Since your array is not flattened, you will need to visit each value and increment unless you want to call merging functions.
Code: (Demo)
$array = [
1 => [1, 12, 2],
2 => [1, 13, 3],
3 => [1, 12, 2],
4 => [1],
5 => [1]
];
// make the generated value available outside of function scope
// \-------------------------------v--------------------------/
array_walk_recursive($array, function($v)use(&$output) { // visit each leafnode
if (isset($output[$v])) { // check if the key has occurred before
++$output[$v]; // increment
} else {
$output[$v] = 1; // declare as 1 on first occurrence
}
});
var_export($output);
Output:
array (
1 => 5,
12 => 2,
2 => 2,
13 => 1,
3 => 1,
)
Or, non-recursively:
foreach ($array as $row) {
foreach ($row as $v) {
if (isset($output[$v])) { // check if the key has occurred before
++$output[$v]; // increment
} else {
$output[$v] = 1; // declare as 1 on first occurrence
}
}
}
Or, a functional one-liner to flatten then count:
var_export(array_count_values(array_reduce($array, 'array_merge', array())));
Or, a functional one-liner with the splat operator to flatten then count:
var_export(array_count_values(array_merge(...$array)));

You can do this quite easily by using an accumulator array and iterating all the elements:
$result = [];
foreach ($data as $row) {
foreach($row as $value) {
$result[$value] = isset($result[$value]) ? $result[$value] + 1 : 1;
}
}
var_dump($result);

You can use call_user_func_array to merge all the individual arrays, and then array_count_values on that result:
$data = array
(array(1, 12, 2),
array(1, 13, 3),
array(1, 12, 2),
array(1),
array(1)
);
print_r(array_count_values(call_user_func_array('array_merge', $data)));
Output:
Array
(
[1] => 5
[12] => 2
[2] => 2
[13] => 1
[3] => 1
)

Related

More efficient way to sort unique values from array of arrays by occurrences in PHP

What would be the most efficient PHP way to get unique values from an array of arrays and sort them by number of occurrences from most frequent to least?
Example input array:
Array
(
[0] => Array
(
[0] => A
[1] => B
[2] => C
[3] => D
)
[1] => Array
(
[0] => A
[1] => C
[2] => D
)
[2] => Array
(
[0] => C
[1] => F
[2] => X
)
)
Would result in this output array:
Array
(
[0] => C // 3
[1] => A // 2
[2] => D // 2
[3] => B // 1
[4] => F // 1
[5] => X // 1
)
The alphabetical order of values with same number of occurrences is not important.
So far I'm merging the array of arrays:
$all_posts = call_user_func_array( 'array_merge', $results );
Then creating a new array where values become keys. And values are the number of times they occur in the original array of arrays.
$posts_by_count = array();
foreach( $all_posts as $apost ) {
$posts_by_count[ $apost ] = 0;
foreach( $results as $tag_posts ) {
if( in_array( $apost, $tag_posts ) ) {
$posts_by_count[ $apost ]++;
}
}
}
Then I can sort by value
arsort($posts_by_count);
And create a new array where keys become values again.
$sorted_posts = array();
foreach($posts_by_count as $k => $v) {
$sorted_posts[] = $k;
}
pprint( $sorted_posts );
What would be a more efficient way to do that?
The simplest I can come up with is to start with the array_merge(), but using the splat (...) to merge all of the arrays. Then use the inbuilt array_count_values() to summarize the values and then arsort() to sort them...
$all_posts = array_merge(...$results );
$posts_by_count = array_count_values($all_posts);
arsort($posts_by_count);
This gives the output of...
Array
(
[C] => 3
[A] => 2
[D] => 2
[B] => 1
[F] => 1
[X] => 1
)
using
print_r(array_keys($posts_by_count));
gives...
Array
(
[0] => C
[1] => A
[2] => D
[3] => B
[4] => F
[5] => X
)
That's simple, first iterate to count occurences, finally use arsort() to sort it by value in descending order:
<?php
// sample data
$arr = [
['A', 'B', 'C', 'D'],
['A', 'C', 'D'],
['C', 'F', 'X']
];
// counting
$newArr = [];
foreach ($arr as $subarr) {
foreach ($subarr as $char) {
$newArr[$char] = (!array_key_exists($char, $newArr))
? 1
: $newArr[$char] = $newArr[$char] + 1;
}
}
// sort by value in DESC order
arsort($newArr);
// To get exactly what you want (without counting) just iterate $newArr and writ is as a $flatArr
$flatArr = [];
foreach ($newArr as $index => $item) {
$flatArr[] = $index;
}
// or with array_keys which _may_ be unstable #see: https://stackoverflow.com/q/10336363/1066240
$flatArrArrayKeys = array_keys($newArr);
// output
$newArrHr = print_r($newArr, 1);
$flatArrHr = print_r($flatArr, 1);
echo "<pre>OUTPUT:
With count:
$newArrHr
As flat array:
$flatArrHr
As flat array with array_keys()
$flatArrArrayKeys
";

Why is the second-to-last value copied onto the last value of the array [duplicate]

This question already has answers here:
PHP Pass by reference in foreach [duplicate]
(9 answers)
Closed 3 years ago.
I am at a beginners level. I was reading the Php.net manual and I found this example there about foreach loop. I have tried my best to understand why does the second to last value gets copied to the last value but still no clue. Please answer in detail.
<?php
$arr = array(1, 2, 3, 4);
foreach($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
// without an unset($value), $value is still a reference to the last item: $arr[3]
foreach($arr as $key => $value) {
// $arr[3] will be updated with each value from $arr...
echo "{$key} => {$value} ";
print_r($arr);
}
// ...until ultimately the second-to-last value is copied onto the last value
// output:
// 0 => 2 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 2 )
// 1 => 4 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 4 )
// 2 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
// 3 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
?>
Sorry for my bad english but it's not my native language.
It's because your second foreach doesn't reinitialize $value, so it's still a reference.
If you var_dump your array you will see this :
array(4) {
[0]=>
int(2)
[1]=>
int(4)
[2]=>
int(6)
[3]=>
&int(8)
}
Your last value is a reference, so it will take the value of $value. 2 on the first iteration, 4 on the second... For the last, it's himself, which have a value of 6 at the last time.

Select and delete random keys from multidimensional array

I have one problem with randomness.
I have example array.
$example=array(
'F1' => array('test','test1','test2','test5'),
'F2' => array('test3', 'test4'),
'F3' => array('one','twoo','threee','x'),
'F5' => array('wow')
)
I want to choose random keys from array to other array with specified size. In this second array I want values from other groups.
For example i got
$amounts = array(4,3,1,2,1);
I want to choose randomly specified ammount of variables ($amount) from $example, but of course - always from other groups.
Example result:
$result=(
array(4) => ('test','test4','x','wow'),
array(3) => ('test2','test3','three'),
array(1) => ('test1')
array(2) => ('test5','one')
array(1) => ('twoo')
What I tried so far?
foreach($amounts as $key=>$amount){
$random_key[$key]=array_rand($example,$amount);
foreach($result[$key] as $key2=>$end){
$todelete=array_rand($example[$end]);
$result[$key][$key2]=$example[$amount][$todelete]
}
}
I don't know now, what to fix or to do next.
Thanks for help!
$example = array(
'F1' => array('test', 'test1', 'test2', 'test5'),
'F2' => array('test3', 'test4'),
'F3' => array('one', 'twoo', 'threee', 'x'),
'F5' => array('wow')
);
$amounts = array(4, 3, 1, 2, 1);
$result = array();
$example = array_values($example);
//randomize the array
shuffle($example);
foreach ($example as $group) {
shuffle($group);
}
//sort the example array by child length
usort($example, function ($a, $b) {
return count($b) - count($a);
});
foreach ($amounts as $amount) {
$tmpResult = array();
for ($i = 0; $i < $amount; $i++) {
if(empty($example[$i])){
throw new \InvalidArgumentException('The total number of values in the array exceed the amount inputed');
}
$tmpResult[] = array_pop($example[$i]);
}
$result[] = $tmpResult;
//sort the example array again by child length
usort($example, function ($a, $b) {
return count($b) - count($a);
});
}
print_r($result);
test result:
Array
(
[0] => Array
(
[0] => test5
[1] => x
[2] => test4
[3] => wow
)
[1] => Array
(
[0] => test2
[1] => threee
[2] => test3
)
[2] => Array
(
[0] => test1
)
[3] => Array
(
[0] => twoo
[1] => test
)
[4] => Array
(
[0] => one
)
)

how to get value from associative array

This is my array :
Array
(
[0] => Array
(
[0] => S No.
[1] => Contact Message
[2] => Name
[3] => Contact Number
[4] => Email ID
)
[1] => Array
(
[0] => 1
[1] => I am interested in your property. Please get in touch with me.
[2] => lopa <br/>(Individual)
[3] => 1234567890
[4] => loperea.ray#Gmail.com
)
[2] => Array
(
[0] => 2
[1] => This user is looking for 3 BHK Multistorey Apartment for Sale in Sohna, Gurgaon and has viewed your contact details.
[2] => shiva <br/>(Individual)
[3] => 2135467890
[4] => sauron82#yahoo.co.in
)
)
How can I retrieve all data element wise?
You can get information about arrays in PHP on the official PHP doc page
You can access arrays using square braces surrounding the key you like to select [key].
So $array[1] will give yoo:
Array
(
[0] => 1
[1] => I am interested in your property. Please get in touch with me.
[2] => lopa <br/>(Individual)
[3] => 1234567890
[4] => loperea.ray#Gmail.com
)
And $array[1][2] will give you:
lopa <br/>(Individual)
Or you can walkt through the elements of an array using loops like the foreach or the for loop.
// perfect for assoc arrays
foreach($array as $key => $element) {
var_dump($key, $element);
}
// alternative for arrays with seamless numeric keys
$elementsCount = count($array);
for($i = 0; $i < $elementsCount; ++$i) {
var_dump($array[$i]);
}
You have integer indexed elements in multidimensional array. To access single element from array, use array name and it's index $myArray[1]. To get inner element of that previous selected array, use second set of [index] - $myArray[1][5] and so on.
To dynamically get all elements from array, use nested foreach loop:
foreach ($myArray as $key => $values) {
foreach ($values as $innerKey => $value) {
echo $value;
// OR
echo $myArray[$key][$innerKey];
}
}
The solution is to use array_reduce:
$header = array_map(
function() { return []; },
array_flip( array_shift( $array ) )
); // headers
array_reduce( $array , function ($carry, $item) {
$i = 0;
foreach( $carry as $k => $v ) {
$carry[$k][] = $item[$i++];
}
return $carry;
}, $header );
First of all we get the header from the very first element of input array. Then we map-reduce the input.
That gives:
$array = [['A', 'B', 'C'], ['a1', 'b1', 'c1'], ['a2', 'b2', 'c2'], ['a3', 'b3', 'c3']];
/*
array(3) {
'A' =>
array(3) {
[0] =>
string(2) "a1"
[1] =>
string(2) "a2"
[2] =>
string(2) "a3"
}
'B' =>
array(3) {
[0] =>
string(2) "b1"
[1] =>
string(2) "b2"
[2] =>
string(2) "b3"
}
'C' =>
array(3) {
[0] =>
string(2) "c1"
[1] =>
string(2) "c2"
[2] =>
string(2) "c3"
}
}
*/
I think this is what you are looking for
$array = Array
(
0=> Array
(
0 => 'S No.',
1 => 'Contact Message',
2 => 'Name',
3 => 'Contact Number',
4 => 'Email ID'
),
1 => Array
(
0 => 1,
1 => 'I am interested in your property. Please get in touch with me.',
2 => 'lopa <br/>(Individual)',
3 => '1234567890',
4 => 'loperea.ray#Gmail.com',
),
2 => Array
(
0 => 2,
1 => 'This user is looking for 3 BHK Multistorey Apartment for Sale in Sohna, Gurgaon and has viewed your contact details.',
2 => 'shiva <br/>(Individual)',
3 => '2135467890',
4 => 'sauron82#yahoo.co.in',
)
);
$result_array = array();
array_shift($array);
reset($array);
foreach($array as $x=>$array2){
foreach($array2 as $i => $arr){
if($i == 1){
$result_array[$x]['Contact Message'] = $arr;
}elseif($i == 2){
$result_array[$x]['Name'] = $arr;
}elseif($i == 3){
$result_array[$x]['Contact Number'] =$arr;
}elseif($i == 4){
$result_array[$x]['Email ID'] = $arr;
}
}
}
print_r($result_array);

How do I use array_unique on an array of arrays?

I have an array
Array(
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[1] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[2] => Array
(
[0] => 33
[user_id] => 33
[1] => 8
[frame_id] => 8
)
[3] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
[4] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
As you can see key 0 is the same as 1, 3 and 4. And key 2 is different from them all.
When running the array_unique function on them, the only left is
Array (
[0] => Array
(
[0] => 33
[user_id] => 33
[1] => 3
[frame_id] => 3
)
)
Any ideas why array_unique isn't working as expected?
It's because array_unique compares items using a string comparison. From the docs:
Note: Two elements are considered
equal if and only if (string) $elem1
=== (string) $elem2. In words: when the string representation is the same.
The first element will be used.
The string representation of an array is simply the word Array, no matter what its contents are.
You can do what you want to do by using the following:
$arr = array(
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 3),
array('user_id' => 33, 'frame_id' => 8)
);
$arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'user' => int 3
2 =>
array
'user_id' => int 33
'user' => int 8
Here's how it works:
Each array item is serialized. This
will be unique based on the array's
contents.
The results of this are run through array_unique,
so only arrays with unique
signatures are left.
array_intersect_key will take
the keys of the unique items from
the map/unique function (since the source array's keys are preserved) and pull
them out of your original source
array.
Here's an improved version of #ryeguy's answer:
<?php
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 5)
);
# $arr = array_intersect_key($arr, array_unique(array_map('serialize', $arr)));
$arr = array_intersect_key($arr, array_unique(array_map(function ($el) {
return $el['user_id'];
}, $arr)));
//result:
array
0 =>
array
'user_id' => int 33
'tmp_id' => int 3
First, it doesn't do unneeded serialization. Second, sometimes attributes may be different even so id is the same.
The trick here is that array_unique() preserves the keys:
$ php -r 'var_dump(array_unique([1, 2, 2, 3]));'
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[3]=>
int(3)
}
This let's array_intersect_key() leave the desired elements.
I've run into it with Google Places API. I was combining results of several requests with different type of objects (think tags). But I got duplicates, since an object may be put into several categories (types). And the method with serialize didn't work, since the attrs were different, namely, photo_reference and reference. Probably these are like temporary ids.
array_unique() only supports multi-dimensional arrays in PHP 5.2.9 and higher.
Instead, you can create a hash of the array and check it for unique-ness.
$hashes = array();
foreach($array as $val) {
$hashes[md5(serialize($val))] = $val;
}
array_unique($hashes);
array_unique deosn't work recursive, so it just thinks "this are all Arrays, let's kill all but one... here we go!"
Quick Answer (TL;DR)
Distinct values may be extracted from PHP Array of AssociativeArrays using foreach
This is a simplistic approach
Detailed Answer
Context
PHP 5.3
PHP Array of AssociativeArrays (tabluar composite data variable)
Alternate name for this composite variable is ArrayOfDictionary (AOD)
Problem
Scenario: DeveloperMarsher has a PHP tabular composite variable
DeveloperMarsher wishes to extract distinct values on a specific name-value pair
In the example below, DeveloperMarsher wishes to get rows for each distinct fname name-value pair
Solution
example01 ;; DeveloperMarsher starts with a tabluar data variable that looks like this
$aodtable = json_decode('[
{
"fname": "homer"
,"lname": "simpson"
},
{
"fname": "homer"
,"lname": "jackson"
},
{
"fname": "homer"
,"lname": "johnson"
},
{
"fname": "bart"
,"lname": "johnson"
},
{
"fname": "bart"
,"lname": "jackson"
},
{
"fname": "bart"
,"lname": "simpson"
},
{
"fname": "fred"
,"lname": "flintstone"
}
]',true);
example01 ;; DeveloperMarsher can extract distinct values with a foreach loop that tracks seen values
$sgfield = 'fname';
$bgnocase = true;
//
$targfield = $sgfield;
$ddseen = Array();
$vout = Array();
foreach ($aodtable as $datarow) {
if( (boolean) $bgnocase == true ){ #$datarow[$targfield] = #strtolower($datarow[$targfield]); }
if( (string) #$ddseen[ $datarow[$targfield] ] == '' ){
$rowout = array_intersect_key($datarow, array_flip(array_keys($datarow)));
$ddseen[ $datarow[$targfield] ] = $datarow[$targfield];
$vout[] = Array( $rowout );
}
}
//;;
print var_export( $vout, true );
Output result
array (
0 =>
array (
0 =>
array (
'fname' => 'homer',
'lname' => 'simpson',
),
),
1 =>
array (
0 =>
array (
'fname' => 'bart',
'lname' => 'johnson',
),
),
2 =>
array (
0 =>
array (
'fname' => 'fred',
'lname' => 'flintstone',
),
),
)
Pitfalls
This solution does not aggregate on fields that are not part of the DISTINCT operation
Arbitrary name-value pairs are returned from arbitrarily chosen distinct rows
Arbitrary sort order of output
Arbitrary handling of letter-case (is capital A distinct from lower-case a ?)
See also
php array_intersect_key
php array_flip
function array_unique_recursive($array)
{
$array = array_unique($array, SORT_REGULAR);
foreach ($array as $key => $elem) {
if (is_array($elem)) {
$array[$key] = array_unique_recursive($elem);
}
}
return $array;
}
Doesn't that do the trick ?
`
$arr = array(
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
array('user_id' => 33, 'tmp_id' => 3),
array('user_id' => 33, 'tmp_id' => 4),
);
$arr1 = array_unique($arr,SORT_REGULAR);
echo "<pre>";
print_r($arr1);
echo "</pre>";
Array(
[0] => Array(
[user_id] => 33
[tmp_id] => 3
)
[1] => Array(
[user_id] => 33
[tmp_id] => 4
)
)
`

Categories