I find a very simple solution to count values in multidimentional arrays (example for 2 levels) :
foreach ($array as $a) {
foreach ($a as $b) {
$count_values[$b]++;
}
}
(PHP 4, PHP 5)
array_count_values — Compte le nombre de valeurs d'un tableau
$array
)
Retourne un tableau contenant les valeurs du tableau
array
comme clés et leur fréquence comme valeurs.
array
Le tableau de valeurs à compter
Retourne un tableau associatif de valeurs ayant les clés correspondant à
array
et leurs nombres comme valeurs.
Lance une alerte de niveau E_WARNING
pour chaque élément qui
n'est pas une chaîne de caractères ou un entier.
Exemple #1 Exemple avec array_count_values()
<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>
L'exemple ci-dessus va afficher :
Array ( [1] => 2 [hello] => 2 [world] => 1 )
I find a very simple solution to count values in multidimentional arrays (example for 2 levels) :
foreach ($array as $a) {
foreach ($a as $b) {
$count_values[$b]++;
}
}
Yet Another case-insensitive version of array_count_values()
<?php
$ar = array('J. Karjalainen', 'J. Karjalainen', 60, '60', 'J. Karjalainen', 'j. karjalainen', 'Fastway', 'FASTWAY', 'Fastway', 'fastway', 'YUP');
$ar = array_count_values(array_map('strtolower', $ar));
?>
<?
function array_icount_values($array) {
$ret_array = array();
foreach($array as $value) $ret_array[strtolower($value)]++;
return $ret_array;
}
$ar = array('J. Karjalainen', 'J. Karjalainen', 60, '60', 'J. Karjalainen', 'j. karjalainen', 'Fastway', 'FASTWAY', 'Fastway', 'fastway', 'YUP');
$ar = array_icount_values($ar);
?>
this prints:
Array
(
[j. karjalainen] => 4
[60] => 2
[fastway] => 4
[yup] => 1
)
In order to apply array_map with callback checking for localised values like city name, country name, you have to provide some sort of comparison array.
Here's an example of array_count_values for Polish city names. Just switch array_keys / array_values in order to obtain lowercase / uppercase.
$arr = array('Warsaw', 'Warsaw', 'Wrocław', 'Poznań', 'KrakÓw', 'waRsaw', 'Gdańsk', 'poznań', 'WROCŁAW', 'kraków', 'GDAŃSK');
$lowertoupperpolish = array(
'ą' => 'Ą',
'ć' => 'Ć',
'ę' => 'Ę',
'ł' => 'Ł',
'ń' => 'Ń',
'ó' => 'Ó',
'ś' => 'Ś',
'ż' => 'Ż',
'ź' => 'Ź',
);
function lowers($n) {
global $lowertoupperpolish;
return strtolower(str_replace(array_values($lowertoupperpolish), array_keys($lowertoupperpolish), $n));
}
$result = array_count_values(array_map('lowers', $arr));
print_r($result); ...prints:
Array ( [warsaw] => 3 [wrocław] => 2 [poznań] => 2 [kraków] => 2 [gdańsk] => 2 )
Here is a Version with one or more arrays, which have similar values in it:
Use $lower=true/false to ignore/set case Sensitiv.
<?php
$ar1[] = array("red","green","yellow","blue");
$ar1[] = array("green","yellow","brown","red","white","yellow");
$ar1[] = array("red","green","brown","blue","black","yellow");
#$ar1= array("red","green","brown","blue","black","red","green"); // Possible with one or multiple Array
$res = array_icount_values ($ar1);
print_r($res);
function array_icount_values($arr,$lower=true) {
$arr2=array();
if(!is_array($arr['0'])){$arr=array($arr);}
foreach($arr as $k=> $v){
foreach($v as $v2){
if($lower==true) {$v2=strtolower($v2);}
if(!isset($arr2[$v2])){
$arr2[$v2]=1;
}else{
$arr2[$v2]++;
}
}
}
return $arr2;
}
/*
Will print:
Array
(
[red] => 3
[green] => 3
[yellow] => 4
[blue] => 2
[brown] => 2
[white] => 1
[black] => 1
)
*/
?>
The case-insensitive version:
<?php
function array_count_values_ci($array) {
$newArray = array();
foreach ($array as $values) {
if (!array_key_exists(strtolower($values), $newArray)) {
$newArray[strtolower($values)] = 0;
}
$newArray[strtolower($values)] += 1;
}
return $newArray;
}
?>
I couldn't find a function for counting the values with case-insensitive matching, so I wrote a quick and dirty solution myself:
<pre><?php
function array_icount_values($array) {
$ret_array = array();
foreach($array as $value) {
foreach($ret_array as $key2 => $value2) {
if(strtolower($key2) == strtolower($value)) {
$ret_array[$key2]++;
continue 2;
}
}
$ret_array[$value] = 1;
}
return $ret_array;
}
$ar = array('J. Karjalainen', 'J. Karjalainen', 60, '60', 'J. Karjalainen', 'j. karjalainen', 'Fastway', 'FASTWAY', 'Fastway', 'fastway', 'YUP');
$ar2 = array_count_values($ar); // Normal matching
$ar = array_icount_values($ar); // Case-insensitive matching
print_r($ar2);
print_r($ar);
?></pre>
This prints:
Array
(
[J. Karjalainen] => 3
[60] => 2
[j. karjalainen] => 1
[Fastway] => 2
[FASTWAY] => 1
[fastway] => 1
[YUP] => 1
)
Array
(
[J. Karjalainen] => 4
[60] => 2
[Fastway] => 4
[YUP] => 1
)
I don't know how efficient it is, but it seems to work. Needed this function in one of my scripts and thought I would share it.
array_count_values function does not work on multidimentional arrays.
If $score[][] is a bidimentional array, the command
"array_count_values ($score)" return the error message "Warning: Can only count STRING and INTEGER values!".
byron at byronrode dot co dot za, here are some benchmarks.
<?php
$haystack = Array();
for ($i = 0; $i < 1000000; $i++) {
$haystack[] = rand(1, 2000);
}
$needle = rand(1, 2000);
echo "__array_count_values()__\n";
$start = microtime(true);
$startmem = memory_get_usage();
$counts = array_count_values($haystack);
$mem = memory_get_usage()-$startmem;
echo 'Count:'.$counts[$needle]."\n";
echo 'Time:'.(microtime(true) - $start)."\n";
echo 'Memory:'.$mem."\n\n";
echo "__array_keys()__\n";
$start = microtime(true);
$startmem = memory_get_usage();
$keys = array_keys($haystack, $needle);
$mem = memory_get_usage()-$startmem;
echo 'Count:'.count($keys)."\n";
echo 'Time:'.(microtime(true) - $start)."\n";
echo 'Memory:'.$mem."\n\n";
echo '__$needle_array[]__'."\n";
$start = microtime(true);
$startmem = memory_get_usage();
$x = count($haystack);
for($i = 0; $i < $x; $i++){
if($haystack[$i] == $needle){
$needle_array[] = $haystack[$i];
}
}
$mem = memory_get_usage()-$startmem;
$number_of_instances = count($needle_array);
echo 'Count:'.$number_of_instances."\n";
echo 'Time:'.(microtime(true) - $start)."\n";
echo 'Memory:'.$mem."\n\n";
echo '__$number_of_instances++__'."\n";
$start = microtime(true);
$startmem = memory_get_usage();
$x = count($haystack);
$number_of_instances = 0;
for($i = 0; $i < $x; $i++){
if($haystack[$i] == $needle){
$number_of_instances++;
}
}
$mem = memory_get_usage()-$startmem;
echo 'Count:'.$number_of_instances."\n";
echo 'Time:'.(microtime(true) - $start)."\n";
echo 'Memory:'.$mem."\n\n";
?>
[www]mytemp$ php array_count_test.php
__array_count_values()__
Count:515
Time:0.0607650279999
Memory:120328
__array_keys()__
Count:515
Time:0.0869138240814
Memory:33016
__$needle_array[]__
Count:515
Time:0.259949922562
Memory:24792
__$number_of_instances++__
Count:515
Time:0.258481025696
Memory:0
However, when you use an array of strings by calling md5(rand(1, 2000)), the performance boosts become less significant:
__array_count_values()__
Count:499
Time:0.491794109344
Memory:184328
__array_keys()__
Count:499
Time:0.36399102211
Memory:30072
__$needle_array[]__
Count:499
Time:0.568728923798
Memory:22104
__$number_of_instances++__
Count:499
Time:0.574353933334
Memory:0
Results are similar for string->string haystacks with foreach traversal.
alwaysdrunk's comment only works if you can trust the client web browser. Using this function doesn't validate that every necessary field exists -- only that every field that was submitted has a value in it. Thus if an attacker wished to force a null value into one of the fields, he could (rather easily) construct a modified form without the field and submit THAT.
Besides, you really ought to be validating each field anyway if you're taking user input.
You might use serialize() to serialize your objects before analyzing their frequency. :)