Remove all duplicate data from a multi-dimensional array [duplicate] - php

This question already has answers here:
Filter/Remove rows where column value is found more than once in a multidimensional array
(4 answers)
Closed 9 months ago.
I'm trying to remove all keys with repeated numbers, I've tried some solutions posted here like the one I'm using in my script but none of it worked for my purpose.
This is my array:
There are at least 4 card IDs repeated.
Array
(
[0] => Array
(
[id_card] => 11883834
[type] => 1
[registed] => 1547610891
)
[1] => Array
(
[id_card] => 20311077
[type] => 1
[registed] => 1547610891
)
[2] => Array
(
[id_card] => 16187903
[type] => 3
[registed] => 1547610891
)
[3] => Array
(
[id_card] => 16354099
[type] => 1
[registed] => 1547610891
)
[4] => Array
(
[id_card] => 21133393
[type] => 4
[registed] => 1547610891
)
[5] => Array
(
[id_card] => 15452852
[type] => 2
[registed] => 1547610891
)
[6] => Array
(
[id_card] => 19775869
[type] => 2
[registed] => 1547610891
)
[7] => Array
(
[id_card] => 20311077
[type] => 1
[registed] => 1547610891
)
[8] => Array
(
[id_card] => 21133393
[type] => 4
[registed] => 1547610891
)
[9] => Array
(
[id_card] => 11883834
[type] => 1
[registed] => 1547610891
)
)
1
At the moment I have something like this:
<?php
$array_data = array_map('unserialize', array_unique(array_map('serialize', $myArray)));
echo '<pre>';
print_r($array_data);
echo '</pre>';
?>
With only 10 keys is working perfectly, but when it goes through this with, for example, 50, 100 keys, no longer works.
Any help appreciated.

So the issue here is that the array_unique solutions expect the entire value to be equivalent - they don't just compare based on your ID field.
Instead, you probably want to loop through the array and keep track of which IDs you've seen, and only take those elements for which you haven't seen the ID before.
function filter_duplicate_id_cards($data) {
$seen = array(); // To keep track of the IDs we've seen
$filtered = array(); // Will hold the result
foreach($data as $item) {
if(array_key_exists($item['id_card'], $seen)) {
// We already encountered this id card before.
continue;
}
// Never-before seen id card, append it to the result and set the key in $seen
$filtered[] = $item;
$seen[$item['id_card']] = TRUE;
}
return $filtered;
}
Note that this uses the map form of a PHP array, rather than just appending seen IDs to the list form and using something like in_array to check if we've seen the key. That's important for performance reasons, especially if we're going to be working on large datasets - in_array is O(n) with the # of items in the array whereas array_key_exists is O(1).
A more generic version of this function would look like this:
function filter_duplicate_field($data, $field) {
$seen = array(); // To keep track of the keys we've seen
$filtered = array(); // Will hold the result
foreach($data as $item) {
if(array_key_exists($item[$field], $seen)) {
// We already encountered this key before.
continue;
}
// Never-before seen key, append it to the result and set the key in $seen
$filtered[] = $item;
$seen[$item[$field]] = TRUE;
}
return $filtered;
}
You could then call it like $result = filter_duplicate_field($data, 'id_card');.

This doesn't preserve the original input order, but because the data is indexed so I'll assume that that doesn't matter.
With just 3 calls in a one-liner, you assign temporary associative keys from back to front to eliminate latter duplicates (because php does not permit duplicate keys), then optionally remove the temporary keys with array_values(). No iterated function calls. No lookup arrays.
Code: (Demo)
$array = [
['id_card' => 11883834, 'type' => 1, 'registed' => 1547610891],
['id_card' => 20311077, 'type' => 1, 'registed' => 1547610891],
['id_card' => 16187903, 'type' => 3, 'registed' => 1547610891],
['id_card' => 16354099, 'type' => 1, 'registed' => 1547610891],
['id_card' => 21133393, 'type' => 4, 'registed' => 1547610891],
['id_card' => 15452852, 'type' => 2, 'registed' => 1547610891],
['id_card' => 19775869, 'type' => 2, 'registed' => 1547610891],
['id_card' => 20311077, 'type' => 1, 'registed' => 1547610891],
['id_card' => 21133393, 'type' => 4, 'registed' => 1547610891],
['id_card' => 11883834, 'type' => 1, 'registed' => 1547610891]
];
var_export(array_values(array_column(array_reverse($array), null, 'id_card')));
If you change you mind about wanting to keep the first occurrence, you can remove array_reverse(). If the first level keys are irrelevant in the output, you can remove array_values(). These changes would allow the solution to be a one function call task.
p.s. "registered"

Related

How to initialize multidimensional associative array with empty values

I want to create and initialize a multidimensional array with known possible keys for the second dimension but no values.
This array will store event_ids (populated dynamically) and for each event_id an array having exactly four different counts (also filled dynamically).
Structure I want to create
Array
(
[0] => Array =================> This index will be the event_id
(
[invitation_not_sent_count] =>
[no_response_yet_count] =>
[will_not_attend_count] =>
[will_attend_count] =>
)
)
What I did so far?
$DataArray = array();
$DataArray[] = array('invitation_not_sent_count' => '',
'no_response_yet_count' => '',
'will_not_attend_count' => '',
'will_attend_count' => '');
And inside the loop I am populating data dynamically like:
$DataArray[$result->getId()]['no_response_yet_count'] = $NRCount;
What I get is:
Array
(
[0] => Array
(
[invitation_not_sent_count] =>
[no_response_yet_count] =>
[will_not_attend_count] =>
[will_attend_count] =>
)
[18569] => Array
(
[no_response_yet_count] => 2
)
[18571] => Array
(
[no_response_yet_count] => 1
)
)
What I want is that if a value is not available in the iteration, its entry should be empty as defined at initialization time. So if all other counts are empty in the data except no_response_yet_count, the array should be:
Expected Output
Array
(
[18569] => Array
(
[invitation_not_sent_count] =>
[no_response_yet_count] => 2
[will_not_attend_count] =>
[will_attend_count] =>
)
[18571] => Array
(
[invitation_not_sent_count] =>
[no_response_yet_count] => 1
[will_not_attend_count] =>
[will_attend_count] =>
)
)
I usually resort to a mapping function at that point:
function pre_map(&$row) {
$row = array
(
'invitation_not_sent_count' => '',
'no_response_yet_count' => '',
'will_not_attend_count' => '',
'will_attend_count' => ''
);
}
Then in the while/for loops:
{
$id = $result->getId();
if (!isset($DataArray[$id])) { pre_map($DataArray[$id]); }
$DataArray[$id]['no_response_yet_count'] = $NRCount;
}
The if (!isset($DataArray[$id])) is to make sure it doesn't wipe out the same index row, in case you happen to re-loop on the same ID. Thus, it will only map it once, then never again in the loop.
There are some other one-line shortcuts like maybe even using array_map(), but I was showing the long version, for full flexibility just in case.

Searching 4 level deep array in PHP

Good evening,
I found myself in a bit of a pickle here, with an overcomplicated (i think) $_SESSION array that is set right after a user logs in and contains the info of all User Groups where that user is on, and also this user type of permissions on Group of Devices where that specific user group is allowed.
Here's the deal with irrelevant info ommited:
Array
(
... other stuff ...
[user_groups] => Array
(
[0] => Array
(
[GroupUsersId] => 4
[GroupUsersName] => XXXX
[idUserType] => 2
[NameTypeUser] => Manager
[DevicesAllowed] => Array
(
[GroupDevicesId] => Array
(
[0] => 2
)
[DevicesOnGroup] => Array
(
[0] => 22,24,16
)
)
)
[1] => Array
(
[GroupUsersId] => 5
[GroupUsersName] => YYYY
[idUserType] => 3
[NameTypeUser] => Guest
[DevicesAllowed] => Array
(
)
)
[2] => Array
(
[GroupUsersId] => 1
[GroupUsersName] => ZZZ
[idUserType] => 1
[NameTypeUser] => Admin
[DevicesAllowed] => Array
(
[GroupDevicesId] => Array
(
[0] => 2
)
[DevicesOnGroup] => Array
(
[0] => 1,5,13,12,17,21,22,24,16
)
)
)
)
... more stuff ...
I need to find out what kind of permissions, if any, does the guy has if trying to browse the device with, let's say, DeviceId = 5. If that particular Id is not on any of the arrays, the user isn't even allowed to see it...
I already tryed to change the code in this question How to search by key=>value in a multidimensional array in PHP, but I guess I'm missing some kind of iteration over the arrays.
Any help?
Cheers and thanks in advance.
Edit: $_SESSION can be changed if needed...
(Updated as per comment below) I might be completely missing your point, but would not just iterative processing of your array help?
$user_groups = array(
0 => array(
'GroupUsersName' => 'XXX',
'NameTypeUser' => 'Admin',
'idUserType' => 3,
'DevicesAllowed' => array(
'DevicesOnGroup' => array(
1, 2, 3
)
)
),
1 => array(
'GroupUsersName' => 'YYY',
'NameTypeUser' => 'ReadOnly',
'idUserType' => 1,
'DevicesAllowed' => array(
'DevicesOnGroup' => array(
3, 4, 5
)
)
)
);
$device = 3;
$right = 0;
foreach ($user_groups as $group) {
if (array_key_exists('DevicesOnGroup', $group['DevicesAllowed'])) {
if (in_array($device, $group['DevicesAllowed']['DevicesOnGroup'])) {
if ($group['idUserType'] > $right) {
$right = $group['idUserType'];
}
}
}
}
print_r($right);
Outputs:
3
If you would ask for device which is in no group, it would return 0 (i.e. no access).
iterate the array like this
$guysDeviceId ;
$bGuyMayPass = false;
foreach($_SESSION["user_group"] as $userGroup ){
if(!isset($userGroup[DevicesAllowed]) || !isset($userGroup[DevicesAllowed][DevicesOnGroup])){
continue;
}
if(in_array($userGroup[DevicesAllowed][DevicesOnGroup], $guysDeviceId ){
$bGuyMayPass= true;
}
}
if($bGuyMayPass){
//login, whatever
}

Group rows on two column values and sum the values of specific key [duplicate]

This question already has answers here:
Group multidimensional array data based on two column values and sum values of one column in each group
(5 answers)
Closed 5 months ago.
I have following array where I am trying to merge the elements which has shelf and weight value as duplicate and sum the value of piece key.
Array
(
[0] => Array
(
[shelf] => Left
[weight] => 10.000
[piece] => 1
)
[1] => Array
(
[shelf] => Right
[weight] => 04.000
[piece] => 12
)
[2] => Array
(
[shelf] => Right
[weight] => 04.000
[piece] => 4
)
[3] => Array
(
[shelf] => Right
[weight] => 07.000
[piece] => 8
)
)
Currently I am getting following desired output with help of following SQL statement by creating the temporary table with following fields shelf, weight and piece and inserting all the four values and parsing the result in PHP with following select query
SELECT shelf, weight, SUM(piece) FROM temp GROUP BY CONCAT(shelf, weight)
Array
(
[0] => Array
(
[shelf] => Left
[weight] => 10.000
[piece] => 1
)
[1] => Array
(
[shelf] => Right
[weight] => 04.000
[piece] => 16
)
[3] => Array
(
[shelf] => Right
[weight] => 07.000
[piece] => 8
)
)
However I am believe that this can be simply achieved by PHP, but can't get my head around. Can somebody please point what I might be missing ?
Note to Moderators and SO Vigilante
Please don't take it personal but before marking or even saying this is duplicate, read my question thoroughly and understand what I am trying to achieve and only then if you agree kindly explain in detail why do you think its duplicate, rather than simply arguing on base of question with similar title
I have gone through these questions, but they don't work in my scenario, as they try to merge array and sum value based on one specific element which is usually either ID, but in my case its uniqueness is judged on combination of elements (not one)
1, 2, 3
If you absolutely have to do this in PHP, then something like:
$data = array(
array(
'shelf' => 'Left',
'weight' => '10.000',
'piece' => 1,
),
array(
'shelf' => 'Right',
'weight' => '04.000',
'piece' => 12,
),
array(
'shelf' => 'Right',
'weight' => '04.000',
'piece' => 4,
),
array(
'shelf' => 'Right',
'weight' => '07.000',
'piece' => 8,
),
);
$newData = array();
$result = array_reduce(
$data,
function($newData, $value) {
$key = $value['shelf'].$value['weight'];
if (!isset($newData[$key])) {
$newData[$key] = $value;
} else {
$newData[$key]['piece'] += $value['piece'];
}
return $newData;
},
$newData
);
var_dump($result);
will work, but I really do believe that you're creating major performance problems for yourself with your whole approach to this problem

Cakephp using Set class to fetch data with key starting at zero

I'm using Set class of Cakephp to format the find returned array but cannot seem to find a way to get the counter starting at zero and auto-increment for array keys so it is like
[0] => 3
[1] => 6
[2] => 12
I'm currently using below query to get the data from my HasAndBelongsToMany table.
$interest_ids = Set::combine($this->User->Interestsub->find('threaded', array
(
'conditions' => array
(
'Interestsub.name' => $interests
),
//'fields' => array('Interestsub.id'),
'recursive' => -1
)
),
'{n}.Interestsub.id',
'{n}.Interestsub.id'
);
The reason why I need this is that I'm currently trying to get the returned array as part of bigger parent array preparing to be saved for SaveAll function. To be formatted properly, I need below nested array coming out:
[0] => Array
(
[interestssub_id] => 12
[user_id] => 2
)
[1] => Array
(
[interestssub_id] => 22
[user_id] => 2
)
[2] => Array
(
[interestssub_id] => 32
[user_id] => 2
)
Is there a way we can use Combine class to format the returned array like above?
There's no real reason to use the Set class in this case. Just use good old fashioned php:
$threaded = $this->User->Interestsub->find('threaded', array(
'conditions' => array(
'Interestsub.name' => $interests
),
'recursive' => -1
));
$interest_ids = array();
foreach ($threaded as $thread) {
$interest_ids[] = array(
'interestssub_id' => $thread['Interestsub.id'],
'interestssub_id' => $thread['Interestsub.user_id']
);
}

Creating arrays dynamically using PHP

Hey folks, please lend a hand to a PHP beginner. I'm trying to put a load of dynamically created variabled into an array to re-read later, reason is a SOAP message sent is a mess and im trying to create a less complicated array:
$placea = "place a";
$placeb = "place b";
$myarray = array();
echo "<pre>";
print_r($myarray);
echo "</pre>";
what i want to be able to do:
Array
(
[0] => [Place A] => Array
(
[0] => [Accommodation] => Array
(
[RoomId] => 001
[RoomAvail] => true
[Date] => 12.04.2011
)
[1] => [Accommodation] => Array
(
[RoomId] => 002
[RoomAvail] => true
[Date] => 12.04.2011
)
) Array
(
[1] => [Place B] => Array
(
[0] => [Accommodation] => Array
(
[RoomId] => 101
[RoomAvail] => true
[Date] => 12.04.2011
)
[1] => [Accommodation] => Array
(
[RoomId] => 102
[RoomAvail] => true
[Date] => 12.04.2011
)
)
)
how would i write that out in php? sorry if its bleek and/or the array structure is incorrect
So you just need to use the array initializer repetitively.
If you want to initialize an array in PHP with some values, say 1 through 4, you make a call like:
$foo = array(1, 2, 3, 4);
And if you want to make an associative array, where you store some key/value pairs, you make a call like:
$foo = array('key' => 'value', 'other key' => 'other value');
But you can of course nest calls, and mix and match layers of associative and non associative arrays to achieve something like your example, e.g.:
$foo = array(
'Place A' => array(
// note use of first array on the next line is
// to generate structure like [0] => 'Accomodation' => ...
array('Accomodation' => array(
'RoomId' => '001',
'RoomAvail' => true,
'Date' => '12.04.2011')
)),
array('Accomodation' => array(
'RoomId' => '002',
'RoomAvail' => true,
'Date' => '12.04.2011')
))
),
'Place B' => array(
array('Accomodation' => array(
'RoomId' => '101',
'RoomAvail' => true,
'Date' => '12.04.2011')
)),
array('Accomodation' => array(
'RoomId' => '102',
'RoomAvail' => true,
'Date' => '12.04.2011')
))
)
);
This will very nearly produce what you're looking for, to make it replicate exactly what you have you would wrap each 'Place A' with an array and each "place" would get its own assignment to some variable $foo (I assumed this wasn't actually what you wanted and wrote something maybe slightly more intuitive).
If you want to have a 'less complicated' array, you have a two arrays, one fore place a and one for place b and then merge them using array_merger() http://www.php.net/manual/en/function.array-merge.php.
Study up on the array functions control structures in the manual. Many different ways of achieving bloated arrays uglying up your code.
This would dynamically create an array.
foreach($soapResponse as $key1 => $value1){
foreach($value as $key2 => $value2){
// $key1 = Place A or B
// value1 = array of values
$arrayResponse[$key1][$key2] = $value2;
}
}

Categories