counting occurence on multiple PHP array [duplicate] - php

This question already has answers here:
Count specific values in multidimensional array
(4 answers)
Closed 9 years ago.
I'm looking for a way to count occurence on an array of array.
This is my array :
Array
(
[0] => Array
(
[id] => 671
[title] => BIEND
[img] =>
[ville] => marseille
)
[1] => Array
(
[id] => 670
[title] => BIENC
[img] =>
[ville] => avignon
)
[2] => Array
(
[id] => 669
[title] => BIENB
[img] =>
[ville] => avignon
)
)
And what I would like to have :
Array
(
[avignon] => 2
[marseille] => 1
)
I tried with array_count_values, but it dont seems to be the good way.
Any idea?

You could just go through it manually:
$result = array();
foreach($input as $item)
{
$result[$item['ville']]++;
}
or, slightly nicer perhaps,
$result = array();
foreach($input as $item)
{
$city = $item['ville'];
if(!array_key_exists($city, $result)) {
$result[$city] = 1;
} else {
$result[$city]++;
}
}
Alternatively, you could do some array_map magic to first get an array with all the cities, and then use array_count_values as you planned:
$cities = array_count_values( array_map( function($a) { return $a['ville']; } ) );
Note, I haven't tested this last solution, I personally think the first one expresses the intention better. If you would like to use this one because it is shorter (i.e. less readable) I'll leave it to you to debug and comment it

You can use array_reduce():
$data = Array
(
0 => Array
(
'id' => 671,
'title' => 'BIEND',
'img' => '',
'ville' => 'marseille'
)
,
1 => Array
(
'id' => 670,
'title' => 'BIENC',
'img' => '',
'ville' => 'avignon'
)
,
2 => Array
(
'id' => 669,
'title' => 'BIENB',
'img' => '',
'ville' => 'avignon'
)
);
$result = array_reduce($data, function(&$cur, $x)
{
$cur[$x['ville']] = isset($cur[$x['ville']])?$cur[$x['ville']]+1:1;
return $cur;
}, []);

$my_array = array(...);
$result = array();
foreach ($my_array as $arr) {
$key = $arr['ville'];
if (! array_key_exists($key, $result){
$result[$key] = 1;
continue;
}
$result[$key] += 1;
}

I would write something like this. Array and subArray should be renamed according to their content.
$villes = array();
foreach($yourArray as $subArray) {
if(!in_array($subArray['ville'], $villes)) {
$villes[$subArray['ville']] = 1;
} else {
$villes[$subArray['ville']]++;
}
}
var_dump($villes);

Related

php merge arrays and change its grouping [duplicate]

I have a multidimensional array and am trying to group them according to the value in a specific column.
I'm trying to group them by level, but I won't actually know the level beforehand. So, it's not like I can put it in a for loop and say while $i < 7, because I won't know that 7 is the maximum value for the level key, and frankly, I'm not sure that's how I would need to do it even if I did.
[
['cust' => 'XT8900', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8944', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8922', 'type' => 'premier', 'level' => 3],
['cust' => 'XT8816', 'type' => 'permier', 'level' => 3],
['cust' => 'XT7434', 'type' => 'standard', 'level' => 7],
]
Desired result:
Array (
[1] => Array (
[0] => Array (
[cust] => XT8900
[type] => standard
)
[1] => Array (
[cust] => XT8944
[type] => standard
)
)
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
)
Best way, if you have control over building the initial array, is just set things up like that at the start as you add entries.
If not then build a temporary array to sort:
foreach ($input_arr as $key => &$entry) {
$level_arr[$entry['level']][$key] = $entry;
}
Leaves you with the form you wanted and everything referenced together.
Build the array like that in the first place though if at all possible.
You need to group them by level first
Use foreach to loop into array check if the level is the same with the previous item then group it with that array
$templevel=0;
$newkey=0;
$grouparr[$templevel]="";
foreach ($items as $key => $val) {
if ($templevel==$val['level']){
$grouparr[$templevel][$newkey]=$val;
} else {
$grouparr[$val['level']][$newkey]=$val;
}
$newkey++;
}
print($grouparr);
The output of print($grouparr); will display like the format you hoped for
You can also try to
print($grouparr[7]);
Will display
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
Or
print($grouparr[3]);
Will display
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
Here is the solution I landed on for an identical problem, wrapped as a function:
function arraySort($input,$sortkey){
foreach ($input as $key=>$val) $output[$val[$sortkey]][]=$val;
return $output;
}
To sort $myarray by the key named "level" just do this:
$myArray = arraySort($myArray,'level');
Or if you didn't want it as a function, just for a one time use, this would create $myNewArray from $myArray grouped by the key 'level'
foreach ($myArray as $key=>$val) $myNewArray[$val['level']][]=$val;
function group_assoc($array, $key) {
$return = array();
foreach($array as $v) {
$return[$v[$key]][] = $v;
}
return $return;
}
//Group the requests by their account_id
$account_requests = group_assoc($requests, 'account_id');
$result = array();
foreach ($yourArrayList as $data) {
$id = $data['level'];
if (isset($result[$id])) {
$result[$id][] = $data;
} else {
$result[$id] = array($data);
}
}
Best ans.
$levels = array_unique(array_column($records, 'level'));
$data = array();
foreach($records as $key => $value){
$data[$levels[array_search($value['level'],$levels )]][] = $value ;
}
print_r($data);
To generate the question's exact desured output from the sample input, pull/pop the last value from each row, use that value as the first level grouping key. Then use the original first level index as the second level key. Then push the two remaining elements into the group's subset.
Code: (Demo)
$result = [];
foreach ($array as $key => $row) {
$result[array_pop($row)][$key] = $row;
}
var_export($result);
For functional style syntax, use array_reduce(). (Demo)
var_export(
array_reduce(
array_keys($array),
function($result, $key) use ($array) {
$result[array_pop($array[$key])][$key] = $array[$key];
return $result;
}
)
);
function _group_by($array,$key,$keyName)
{
$return = array();
foreach($array as $val) {
$return[$keyName.$val[$key]][] = $val;
}
return $return;
} //end of function

How to merge an array with child elements

I have an array with same customerid. I want to merge all same customerid arrays in to one with few amends to the array.
Array
(
[0] => Array
(
[customerid] => 13
[customer_fullname] => Chris
[profession_id] => 8
[profession_name] => Producer
)
[1] => Array
(
[customerid] => 1
[customer_fullname] => John
[profession_id] => 8
[profession_name] => Producer
)
[2] => Array
(
[customerid] => 13
[customer_fullname] => Chris
[profession_id] => 7
[profession_name] => Camera
)
)
So now I want a new array to be created like this:
Array(
[customerid] => 13
[customer_fullname] => Chris
[new_array] => array(
[0]=>[profession_id] => 8, [profession_name] => Producer,
[1]=>[profession_id] => 7, [profession_name] => Camera
)
)
Spent some time on it but wasn't able to get it right
There are better approaches if you're merging lots of records, but if you want a way to just merge two records as stated, I'd just do this:
$array1 = array(
'customerid' => 13
'customer_fullname' => 'John',
'profession_id' => 8,
'profession_name' => 'Producer'
);
$array2 = array(
'customerid' => 13
'customer_fullname' => 'John',
'profession_id' => 7,
'profession_name' => 'Director'
);
function merge_customers($customerA, $customerB)
{
$newCustomer = array();
if ($customerA['customerid'] == $customerB['customerid'])
{
$newCustomer['customerid'] = $customerA['customerid'];
$newCustomer['customer_fullname'] = $customerA['customer_fullname'];
$newCustomer['new_array'] = array(
array(
'profession_id' => $customerA['profession_id'],
'profession_name' => $customerA['profession_name']
),
array(
'profession_id' => $customerB['profession_id'],
'profession_name' => $customerB['profession_name']
)
);
return $newCustomer;
}
/* We can't merge these if they're different customers. */
return NULL;
}
The extended solution which is also well-suited for finding and "merging" multiple groups of entries which has same customerid. Used functions: array_filter, array_count_values, array_keys, array_walk, array_chunk and array_values:
// supposing $arr is your initial array
// finds which 'customerid' has multiple entries
$dupIds = array_filter(array_count_values(array_column($arr, "customerid")), function($v) {
return $v > 1;
});
$dupIds = array_keys($dupIds);
$result = [];
array_walk($arr, function($v) use(&$result, $dupIds) {
if (in_array($v['customerid'], $dupIds)) {
$parts = array_chunk($v, 2, true);
if (!isset($result[$v['customerid']])) {
$result[$v['customerid']] = $parts[0] + ['new_array' => [$parts[1]]];
} else {
$result[$v['customerid']]['new_array'][] = $parts[1];
}
}
});
print_r(array_values($result));
The output:
Array
(
[0] => Array
(
[customerid] => 13
[customer_fullname] => Chris
[new_array] => Array
(
[0] => Array
(
[profession_id] => 8
[profession_name] => Producer
)
[1] => Array
(
[profession_id] => 7
[profession_name] => Camera
)
)
)
)
Quick hack, maybe there is a nicer solution.
Note: The second "for each" loop is only needed if there is the possibility that the arrays don't have the same fields.
function merge($array1, $array2){
$result = array();
foreach($array1 as $key => $value){
if(isset($array2[$key]) && $array2[$key]!=$array1[$key]){
$result[$key][]=$value;
$result[$key][]=$array2[$key];
}else{
$result[$key]=$value;
}
}
foreach($array2 as $key => $value){
if(!isset($result[$key])){
$result[$key] = $value;
}
}
return $result;
}
print_r(merge($array1, $array2));

PHP Counting inside an Array

I want to create a list where if its already in the array to add to the value +1.
Current Output
[1] => Array
(
[source] => 397
[value] => 1
)
[2] => Array
(
[source] => 397
[value] => 1
)
[3] => Array
(
[source] => 1314
[value] => 1
)
What I want to Achieve
[1] => Array
(
[source] => 397
[value] => 2
)
[2] => Array
(
[source] => 1314
[value] => 1
)
My current dulled down PHP
foreach ($submissions as $timefix) {
//Start countng
$data = array(
'source' => $timefix['parent']['id'],
'value' => '1'
);
$dataJson[] = $data;
}
print_r($dataJson);
Simply use an associated array:
$dataJson = array();
foreach ($submissions as $timefix) {
$id = $timefix['parent']['id'];
if (!isset($dataJson[$id])) {
$dataJson[$id] = array('source' => $id, 'value' => 1);
} else {
$dataJson[$id]['value']++;
}
}
$dataJson = array_values($dataJson); // reset the keys - you don't nessesarily need this
This is not exactly your desired output, as the array keys are not preserved, but if it suits you, you could use the item ID as the array key. This would simplify your code to the point of not needing to loop through the already available results:
foreach ($submissions as $timefix) {
$id = $timefix['parent']['id'];
if (array_key_exists($id, $dataJson)) {
$dataJson[$id]["value"]++;
} else {
$dataJson[$id] = [
"source" => $id,
"value" => 1
];
}
}
print_r($dataJson);
You should simplify this for yourself. Something like:
<?
$res = Array();
foreach ($original as $item) {
if (!isset($res[$item['source']])) $res[$item['source']] = $item['value'];
else $res[$item['source']] += $item['value'];
}
?>
After this, you will have array $res which will be something like:
Array(
[397] => 2,
[1314] => 1
)
Then, if you really need the format specified, you can use something like:
<?
$final = Array();
foreach ($res as $source=>$value) $final[] = Array(
'source' => $source,
'value' => $value
);
?>
This code will do the counting and produce a $new array as described in your example.
$data = array(
array('source' => 397, 'value' => 1),
array('source' => 397, 'value' => 1),
array('source' => 1314, 'value' => 1),
);
$new = array();
foreach ($data as $item)
{
$source = $item['source'];
if (isset($new[$source]))
$new[$source]['value'] += $item['value'];
else
$new[$source] = $item;
}
$new = array_values($new);
PHP has a function called array_count_values for that. May be you can use it
Example:
<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>
Output:
Array
(
[1] => 2
[hello] => 2
[world] => 1
)

Sorting an array according to some duplicate values in PHP

I have an array containing strings of this format:
[0] => "title|url|score|user|date"
[1] => "title|url|score|user|date"
[2] => "title|url|score|user|date"
[3] => "title|url|score|user|date"
...
The score field is an int that is not always unique (for example, more than one entry can have a score of 0). I'm looking to sort the strings in this array based on their score value. Originally, I tried iterating through the array and making a new one with keys corresponding to the vote score. I soon realized that you can't have duplicate keys in an array.
Is there a good clean way of doing this?
$array = array(
0 => "title|url|12|user|date",
1 => "title|url|0|user|date",
2 => "title|url|13|user|date",
3 => "title|url|0|user|date"
);
function sortOnScore( $a, $b )
{
// discard first two values
list( ,,$scoreA ) = explode( '|', $a );
list( ,,$scoreB ) = explode( '|', $b );
return $scoreA == $scoreB ? 0 : ( $scoreA > $scoreB ? 1 : -1 );
}
usort( $array, 'sortOnScore' );
var_dump( $array );
Look into PHP's usort function
function score_sort($rec1, $rec2)
{
return $rec1['score'] - $rec2['score'];
}
usort($score_array);
Replace ['score'] with however you are extracting the scores from the strings
First you need to turn the strings into arrays with explode so you can do the comparisons:
// If using PHP >= 5.3, this can also be made into an anonymous function
function converter($string) {
$result = array_combine(
array('title', 'url', 'score', 'user', 'date'),
explode('|', $string)
);
// When these are later compared, it should be as numbers
$result['score'] = (int)$result['score'];
return $result;
}
$input = array(
'Foo|http://foo|0|user1|today',
// etc.
);
$converted = array_map('converter', $input);
This will make $converted look like:
array (
0 => array (
'title' => 'Foo',
'url' => 'http://foo',
'score' => '0',
'user' => 'user1',
'date' => 'today',
),
)
Then you can sort the array using the code from my answer here by easily specifying any sort criteria you want:
usort($converted, make_converter('score', 'date', 'title'));
Personally I'd be tempted to iterate through the array, split it by the |'s and put it into a new multi-dimensional array, for example something like this:
[0] => array([title]=>'title',[url]=>'url',[score]=>'score',[user]=>'user',[date]=>'date')
[1] => array([title]=>'title',[url]=>'url',[score]=>'score',[user]=>'user',[date]=>'date')
Then it becomes easy to sort, just use a function like this:
function sortmulti ($array, $index, $order, $natsort=FALSE, $case_sensitive=FALSE) {
if(is_array($array) && count($array)>0) {
foreach(array_keys($array) as $key) {
$temp[$key]=$array[$key][$index];
}
if(!$natsort) {
if ($order=='asc') {
asort($temp);
} else {
arsort($temp);
}
}
else
{
if ($case_sensitive===true) {
natsort($temp);
} else {
natcasesort($temp);
}
if($order!='asc') {
$temp=array_reverse($temp,TRUE);
}
}
foreach(array_keys($temp) as $key) {
if (is_numeric($key)) {
$sorted[]=$array[$key];
} else {
$sorted[$key]=$array[$key];
}
}
return $sorted;
}
return $sorted;
}
i.e. do this:
$sortedarray = sortmulti($array,'score','asc');
It would be very easy using the asort function:
$pattern = '#^([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)$#';
$sorted = array();
foreach($data as $s) $sorted[] = preg_replace($pattern, '$3|$1|$2|$4|$5', $s);
asort($sorted);
these 4 lines of code, when given $data:
Array
(
[0] => title_0|url_0|6|user_0|date_0
[1] => title_1|url_1|6|user_1|date_1
[2] => title_2|url_2|2|user_2|date_2
[3] => title_3|url_3|3|user_3|date_3
[4] => title_4|url_4|2|user_4|date_4
[5] => title_5|url_5|7|user_5|date_5
[6] => title_6|url_6|3|user_6|date_6
[7] => title_7|url_7|8|user_7|date_7
[8] => title_8|url_8|3|user_8|date_8
[9] => title_9|url_9|9|user_9|date_9
)
will generate $sorted:
Array
(
[2] => 2|title_2|url_2|user_2|date_2
[4] => 2|title_4|url_4|user_4|date_4
[3] => 3|title_3|url_3|user_3|date_3
[6] => 3|title_6|url_6|user_6|date_6
[8] => 3|title_8|url_8|user_8|date_8
[0] => 6|title_0|url_0|user_0|date_0
[1] => 6|title_1|url_1|user_1|date_1
[5] => 7|title_5|url_5|user_5|date_5
[7] => 8|title_7|url_7|user_7|date_7
[9] => 9|title_9|url_9|user_9|date_9
)
and with just 2 more lines you can have the items in each element of the array back in the original order/format:
$data = array();
foreach($sorted as $s) $data[] = preg_replace($pattern, '$2|$3|$1|$4|$5', $s);
setting $data to:
Array
(
[0] => title_2|url_2|2|user_2|date_2
[1] => title_4|url_4|2|user_4|date_4
[2] => title_3|url_3|3|user_3|date_3
[3] => title_6|url_6|3|user_6|date_6
[4] => title_8|url_8|3|user_8|date_8
[5] => title_0|url_0|6|user_0|date_0
[6] => title_1|url_1|6|user_1|date_1
[7] => title_5|url_5|7|user_5|date_5
[8] => title_7|url_7|8|user_7|date_7
[9] => title_9|url_9|9|user_9|date_9
)
Create a new array of arrays:
[0] => array("score", old_array[0])
Then sort.

Group 2d array data using column value to create a 3d array

I have a multidimensional array and am trying to group them according to the value in a specific column.
I'm trying to group them by level, but I won't actually know the level beforehand. So, it's not like I can put it in a for loop and say while $i < 7, because I won't know that 7 is the maximum value for the level key, and frankly, I'm not sure that's how I would need to do it even if I did.
[
['cust' => 'XT8900', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8944', 'type' => 'standard', 'level' => 1],
['cust' => 'XT8922', 'type' => 'premier', 'level' => 3],
['cust' => 'XT8816', 'type' => 'permier', 'level' => 3],
['cust' => 'XT7434', 'type' => 'standard', 'level' => 7],
]
Desired result:
Array (
[1] => Array (
[0] => Array (
[cust] => XT8900
[type] => standard
)
[1] => Array (
[cust] => XT8944
[type] => standard
)
)
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
)
Best way, if you have control over building the initial array, is just set things up like that at the start as you add entries.
If not then build a temporary array to sort:
foreach ($input_arr as $key => &$entry) {
$level_arr[$entry['level']][$key] = $entry;
}
Leaves you with the form you wanted and everything referenced together.
Build the array like that in the first place though if at all possible.
You need to group them by level first
Use foreach to loop into array check if the level is the same with the previous item then group it with that array
$templevel=0;
$newkey=0;
$grouparr[$templevel]="";
foreach ($items as $key => $val) {
if ($templevel==$val['level']){
$grouparr[$templevel][$newkey]=$val;
} else {
$grouparr[$val['level']][$newkey]=$val;
}
$newkey++;
}
print($grouparr);
The output of print($grouparr); will display like the format you hoped for
You can also try to
print($grouparr[7]);
Will display
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
Or
print($grouparr[3]);
Will display
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
Here is the solution I landed on for an identical problem, wrapped as a function:
function arraySort($input,$sortkey){
foreach ($input as $key=>$val) $output[$val[$sortkey]][]=$val;
return $output;
}
To sort $myarray by the key named "level" just do this:
$myArray = arraySort($myArray,'level');
Or if you didn't want it as a function, just for a one time use, this would create $myNewArray from $myArray grouped by the key 'level'
foreach ($myArray as $key=>$val) $myNewArray[$val['level']][]=$val;
function group_assoc($array, $key) {
$return = array();
foreach($array as $v) {
$return[$v[$key]][] = $v;
}
return $return;
}
//Group the requests by their account_id
$account_requests = group_assoc($requests, 'account_id');
$result = array();
foreach ($yourArrayList as $data) {
$id = $data['level'];
if (isset($result[$id])) {
$result[$id][] = $data;
} else {
$result[$id] = array($data);
}
}
Best ans.
$levels = array_unique(array_column($records, 'level'));
$data = array();
foreach($records as $key => $value){
$data[$levels[array_search($value['level'],$levels )]][] = $value ;
}
print_r($data);
To generate the question's exact desured output from the sample input, pull/pop the last value from each row, use that value as the first level grouping key. Then use the original first level index as the second level key. Then push the two remaining elements into the group's subset.
Code: (Demo)
$result = [];
foreach ($array as $key => $row) {
$result[array_pop($row)][$key] = $row;
}
var_export($result);
For functional style syntax, use array_reduce(). (Demo)
var_export(
array_reduce(
array_keys($array),
function($result, $key) use ($array) {
$result[array_pop($array[$key])][$key] = $array[$key];
return $result;
}
)
);
function _group_by($array,$key,$keyName)
{
$return = array();
foreach($array as $val) {
$return[$keyName.$val[$key]][] = $val;
}
return $return;
} //end of function

Categories