Counting distinct values in multidimensional array - php

I'm having real problems trying to figure this one out.
I have a PHP array which looks like this:
$info = array();
$info[0] = array(
'car' => 'Audi',
'previous_car' => 'BMW'
);
$info[1] = array(
'car' => 'Audi',
'previous_car' => 'Seat'
);
$info[2] = array(
'car' => 'Audi',
'previous_carg' => 'BMW'
);
$info[3] = array(
'car' => 'BMW',
'previous_car' => 'BMW'
);
$info[4] = array(
'car' => 'Ford',
'previous_car' => 'Seat'
);
I need to do some sorting on this, so the result looks like this:
Array (
car [
'Audi' => 3,
'BMW' => 1,
'Ford' => 1
],
previous_car [
'BMW' => 3,
'Seat' => 2
]
);
I need to count distinct occurrences of a value in the same key, but the search is made upon couple of arrays. I was trying to use array_value_count(), but I doesn't work well on multidimensional arrays.
I am trying to avoid the looping, since it can be overkill if the array is large.
I will be very grateful for all the help.

If you're running PHP 5.5, you can use:
$newArray = array(
'car' => array_count_values(array_column($info, 'car')),
'previous_car' => array_count_values(array_column($info, 'previous_car'))
);
var_dump($newArray);
For versions of PHP prior to 5.5
$newArray = array(
'car' => array_count_values(
array_map(
function($value) {
return $value['car'];
},
$info
)
),
'previous_car' => array_count_values(
array_map(
function($value) {
return $value['previous_car'];
},
$info
)
)
);
var_dump($newArray);

In a more object orientated way you can solve it as follows
$values = new ArrayObject();
$iterator = new RecursiveArrayIterator($info);
iterator_apply($iterator, 'countDistinct', array($iterator, $values));
function countDistinct($iterator, $values) {
while ( $iterator -> valid() ) {
if ( $iterator -> hasChildren() ) {
countDistinct($iterator -> getChildren(), $values);
} else {
if (!$values->offsetExists($iterator->key())) {
$values->offsetSet($iterator->key(), new ArrayObject());
}
if (!$values->offsetGet($iterator->key())->offsetExists($iterator->current())) {
$values->offsetGet($iterator->key())
->offsetSet($iterator->current(), 1);
} else {
$values->offsetGet($iterator->key())
->offsetSet($iterator->current(),
$values->offsetGet($iterator->key())->offsetGet($iterator->current()) + 1);
}
}
$iterator -> next();
}
}
Sure, with this example you do not avoid the loop. But with the ArrayObject and the RecursiveArrayIterator you will have some memory and performance advantages.
The result of this will exactly match your expected result, which you can easyliy iterate with the getIterator() function of the ArrayObject.

You can write a function that will sort your data but for now check this out:
http://www.php.net/manual/en/function.array-multisort.php

Here is what might help you:
$returnArray = array('car' => NULL, 'previous_car' => NULL);
foreach($info as $newInfo) {
$returnArray['car'][] = $newInfo['car'];
$returnArray['previous_car'][] = $newInfo['previous_car'];
}
$ret['car'] = array_count_values($returnArray['car']);
$ret['previous_car'] = array_count_values($returnArray['previous_car']);
var_dump($ret);
This returns:
array (size=2)
'car' =>
array (size=3)
'Audi' => int 3
'BMW' => int 1
'Ford' => int 1
'previous_car' =>
array (size=2)
'BMW' => int 3
'Seat' => int 2

Related

How to check if duplicate values of some indexes exist in multi dimensional array or not?

My problem is i have a multidimensional array posting from form to php, now i want to checking if duplicate values of some indexes exist in multi dimensional array or not?
e.g:
$data=Array
(
0 => Array
(
uid => '100',
name => 'Sandra Shush',
type => 'abc'
),
1 => Array
(
uid => '101',
name => 'Sandra Shushtext',
type => 'xyz'
),
2 => Array
(
uid => '100',
name => 'Sandra Shush',
type => 'abc'
)
);
here name and type of index 1 and 2 are same, so how can i check it?
I am familiar with
$key = array_search('abc', array_column($data, 'type'));
but it is for duplication of single column value in multi rows, in my situation if multi column of same rows same with multi column of any other row then record will be consider as duplicate.
Any help should be appreciated, Thanks in advance.
You can try using array_reduce by creating a key using your desired item keys:
$result = array_reduce($data, function ($carry, $item) {
$key = $item['uid'] . $item['type'];
$item['is_duplicate'] = isset($carry[$key]);
if ($item['is_duplicate']) {
$carry[] = $item;
} else {
$carry[$key] = $item;
}
return $carry;
}, []);
var_dump($result);
The easiest way, well at least the one I would use is to encode your arrays into md5 (or any other kind) string and compare those values. I think it is the most efficient in your case.
Example:
<?php
function arrayToString($array) {
$str = '';
if ( !is_array($array) )
return $str;
foreach ( $array as $key => $val ) {
$str .= $key . ':' . $val;
}
return $str;
}
$data=Array
(
0 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'abc'
),
1 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'xyz'
),
2 => Array
(
'uid' => '100',
'name' => 'Sandra Shush',
'type' => 'abc'
)
);
$temp = array();
foreach ( $data as $d ) {
array_push($temp, md5(arrayToString($d)));
}
$unique = array_unique($temp);
var_dump($unique); // prints unique array
This is a very fast designed approach and will find duplicates. Note that duplicates are elements which have the same value for the same key. So if any of uid, name or type match, they will be treated as duplicates. Therefore I adjust the third array element, because all elements in your array share the same values.
$data = [
....
2 =>
[
'uid' => '200',
'name' => 'Mandra Shush',
'type' => 'abcx'
]
];
$duplicates = [];
$valuesToCompare = ["uid", "name", "type"];
function equals($value, $toCompare, $keysToCompare)
{
foreach ($keysToCompare as $compareKey) {
if ($value[$compareKey] === $toCompare[$compareKey]) {
return true;
}
}
return false;
}
foreach ($data as $index => $element) {
foreach ($data as $indexInner => $elementToCompare) {
if ($index !== $indexInner) {
if (equals($element, $elementToCompare, $valuesToCompare)) {
$duplicates[] = [$index => $indexInner];
}
}
}
}
var_dump($duplicates);
This will output the following, which indicates we found 2 duplicates. Where element 0 is duplicate of 1, and 1 is duplicate of 0.
array (size=2)
0 =>
array (size=1)
0 => int 1
1 =>
array (size=1)
1 => int 0
I achieved above scenario like this:
Dont know which one is best mine or other's who posted answers.
foreach($data as $key => $row)
{
$combinedarr[] = array("name"=>$row["name"],"type"=>$row["type"]);
}
//chck if same facilitiy is being visit on same date twice
$countcomb = count($combinedarr);
$uniquearr = array_unique($combinedarr, SORT_REGULAR);
if($countcomb==count($uniquearr)){
}else{
//yes duplicate exists
};
Thanks again for those who answered.

How to extract the relevant elements from this array of associative arrays?

I have the following challenging array of associative arrays in php.
array(
(int) 0 => array(
'table' => array(
'venue' => 'venue1',
'name' => 'name1'
)
),
(int) 1 => array(
'table' => array(
'venue' => 'venue1',
'name' => 'name2'
)
),
(int) 2 => array(
'table' => array(
'venue' => 'venue2',
'name' => 'name3'
)
),
(int) 3 => array(
'table' => array(
'venue' => 'venue3',
'name' => 'name4'
)
)
)
I want to extract a list of relevant names out based on the venue. I would like to implement a function ExtractNameArray($venue) such that when $venue=='venue1', the returned array will look like array('name1', 'name2')
I have been cracking my head over this. I am starting with $foreach and am stuck. How can this be done in php? Thank you very much.
first, you have to pass the array with the data to the function
second, the name of the function should start with lower character (php conventions)
try this
function extractNameArray($array, $venue) {
$results = array();
foreach($array as $key=>$value) {
if(isset($value['table']['venue'])&&$value['table']['venue']==$venue) {
isset($value['table']['name']) && $results[] = $value['table']['name'];
}
}
return $results;
}
function ExtractNameArray($venue)
{
$array = array(); // your array
$return_array = array();
foreach($array as $arr)
{
foreach($arr['table'] as $table)
{
if($table['venue'] == $venue)
{
$return_array[]['name'] = $table['name'];
}
}
}
return $return_array;
}
You must define $array with you array. Good Luck

What PHP Array Function do I use to convert a multidimensional associative array

I want to convert this.
$data1 = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
into this
$data2 = array(
'value' => array(0 => '100.00', 1 => '10.00', 2 => '200.00', 3 => '190.00'),
'total' => array(0 => '32', 1 => '13', 2 => '39', 3 => '11')
);
I can obviously do this in a roundabout way by iterating over the top array, while appending to a series of arrays, but I figured that there must be a php array function that I don't know about that can do this more concisely.
http://www.php.net/manual/en/ref.array.php
Values are floats and integers (if it makes any difference), I've just added them as strings in the example code because it's easier to read IMO. Final array order should match the initial order. I'll award the correct answer to the least LOC providing performance isn't significantly worse that the 'long' version. PHP 5.4.
If PHP had an array_pluck function, it would be simple.
function array_pluck(array $array, $field)
{
return array_map(function($row) use ($field) { return $row[$field]; }, $array);
}
$data2 = array(
'value' => array_pluck($data1, 'value'),
'total' => array_pluck($data1, 'total')
);
I think that's about as easy as it is to read, but you'll be looping over the entire array once per field, so it's hardly the optimal solution.
Personally, this is a situation where I'd probably stick with the foreach solution but try to wrap it inside some reusable function.
<?php // php 5.4 array syntax
$new = array_reduce($data1, function (&$result, $item)
{
$result['value'][] = $item['value'];
$result['total'][] = $item['total'];
return $result;
},
['value' => [], 'total' => []]);
'value' and 'total' are arbitrary names, so you're not going to get a one-liner php library function to do this.
You can refactor this code into a function if you want to...
function array_rotate($data) {
$k = array_keys($data[0]);
return array_reduce($data, function (&$r, $i) use ($k) {
$r[$k[0]][] = $i[$k[0]];
$r[$k[1]][] = $i[$k[1]];
return $r;
}, [$k[0] => [], $k[1] => []]);
}
I generalised #matthew's code, this allows an arbitrary number of keys (instead of 2):
function array_rotate2($data) {
return array_combine(array_keys($data[0]),
array_map(function ($field) use ($data) {
return array_map(function($row) use ($field) { return $row[$field]; }, $data);
}, array_keys($data[0])));
}
You can do it without a function using a simple foreach():
<?php
$data = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
$newArray = array();
$i=0;
foreach($data as $value){
$newArray["value"][] = $data[$i]["value"];
$newArray["total"][] = $data[$i]["total"];
$i++;
}
echo "<pre>";
print_r($newArray);
echo "</pre>";
?>
Prints this:
Array
(
[value] => Array
(
[0] => 100.00
[1] => 10.00
[2] => 200.00
[3] => 190.00
)
[total] => Array
(
[0] => 32
[1] => 13
[2] => 39
[3] => 11
)
)
$final = array();
foreach($data1 as $array) {
foreach($array as $key => $value) {
$final[$key] = isset($final[$key]) ? $final : array();
$final[$key][] = $value;
}
}
Like others are saying I don't think there is a one-liner. Here is a reusable foreach function that should work
function array_multi_key_combine($a, $keys = array()) {
$b = array();
foreach($a as $v) {
foreach($keys as $k) {
if(isset($v[$k])) $b[$k][] = $v[$k];
}
}
return $b;
}
$data2 = array_multi_key_combine($data1, array('value', 'total'));

PHP: how to create associative array by key?

I have simple array
array(
array( 'id'=>5, 'something' => 2, 'dsadsa' => 'fsfsd )
array( 'id'=>20, 'something' => 2, 'dsadsa' => 'fsfsd )
array( 'id'=>30, 'something' => 2, 'dsadsa' => 'fsfsd )
)
How to create associative array by id field (or something else) from it in the right way?
array(
'5' => array( 'something' => 2, 'dsadsa' => 'fsfsd )
'20' => array( 'something' => 2, 'dsadsa' => 'fsfsd )
'30' => array( 'something' => 2, 'dsadsa' => 'fsfsd )
)
Something along these lines.
$new_array = array();
foreach ($original_array as &$slice)
{
$id = (string) $slice['id'];
unset($slice['id']);
$new_array[$id] = $slice;
}
#NikitaKuhta, nope. There is no slice function which returns a column of values in a 2D keyed table associated with a given key or column heading. You can use some of the callback array_... functions, but you will still need to execute a custom function per element so its just not worth it. I don't like Core Xii's solution as this corrupts the original array as a side effect. I suggest that you don't use references here:
$new_array = array();
foreach ($original_array as $slice) {
$id = (string) $slice['id'];
unset($slice['id']);
$new_array[$id] = $slice;
}
# And now you don't need the missing unset( $slice)

PHP multidimensional array conversion using array_map/array_walk functions

I have the following multidimensional array:
$userList = array(
0 => array('id' => 1000, 'first_name' => 'John', 'last_name' => 'Smith'),
1 => array('id' => 1001, 'first_name' => 'Sam', 'last_name' => 'Johnson'),
);
I want to convert it to the array like:
$userData = array(
1000 => 'John Smith',
1001 => 'Sam Johnson',
);
It's quite obvious for me how to implement this using foreach loop, but I wonder if it's possible to do this with PHP array functions like array_map or array_walk. Please use PHP 5.3 for the callback function. Thank you!
Since those functions only work on the array values, getting them to set the key in the new array is somewhat awkward. One way to do it is:
$arr = array_combine(
array_map(function ($i) { return $i['id']; }, $arr),
array_map(function ($i) { return "$i[first_name] $i[last_name]"; }, $arr)
);
This is a case where a foreach is much more appropriate.
A minor trick from the functional programming.
$arr = array_reduce(
$arr,
function ($result, $item) {
$result[$item['id']] = $item['first_name'] . ' ' . $item['last_name'];
return $result;
},
array()
);
See array_reduce().
Code:
<?php
function convertArray ( $array ) {
$newUserList = array();
foreach ( $array as $value ) {
$newUserList[ $value[ 'id' ] ] = $value[ 'first_name' ] . ' ' . $value[ 'last_name' ];
}
return $newUserList;
}
$userList = array(
0 => array( 'id' => 1000, 'first_name' => 'John', 'last_name' => 'Smith' ),
1 => array( 'id' => 1001, 'first_name' => 'Sam', 'last_name' => 'Johnson' )
);
$newUserList = convertArray( $userList );
?>
Output:
Array
(
[1000] => John Smith
[1001] => Sam Johnson
)

Categories