Merging duplicate arrays with count - php
I'm working with large sets of data and i need to merge the arrays when they are duplicate. If they get merged I need a count added to the array.
array:3721 [▼
0 => array:3 [▼
"subscriber" => "gmail.com."
"code" => 554
"status" => 50
]
1 => array:3 [▼
"subscriber" => "apied.be"
"code" => 550
"status" => 51
]
2 => array:3 [▼
"subscriber" => "beton-dobbelaere.be"
"code" => 550
"status" => 50
]
3 => array:3 [▼
"subscriber" => "live.be"
"code" => 550
"status" => 51
]
4 => array:3 [▼
"subscriber" => "hotmail.be"
"code" => 550
"status" => 51
]
5 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 50
]
6 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 55
]
7 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 51
]
8 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 51
]
This should look something like:
array:3721 [▼
0 => array:3 [▼
"subscriber" => "gmail.com."
"code" => 554
"status" => 50
"amount" => 1
]
1 => array:3 [▼
"subscriber" => "apied.be"
"code" => 550
"status" => 51
"amount" => 1
]
2 => array:3 [▼
"subscriber" => "beton-dobbelaere.be"
"code" => 550
"status" => 50
"amount" => 1
]
3 => array:3 [▼
"subscriber" => "live.be"
"code" => 550
"status" => 51
"amount" => 1
]
4 => array:3 [▼
"subscriber" => "hotmail.be"
"code" => 550
"status" => 51
"amount" => 1
]
5 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 50
"amount" => 1
]
6 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 55
"amount" => 1
]
7 => array:3 [▼
"subscriber" => "telenet.be"
"code" => 550
"status" => 51
"amount" => 2
]
When I merged this example using
array_unique($hardbounces, SORT_REGULAR);
I was left with around 534 results instead of 3721 this is great, only I need to know the amount as well and it has to be somewhat efficient because the result sets can be very big (much bigger).
After it need to be sorted as well to domains and amounts.
I'm using laravel 5.1 if necessary I could turn the array into a collection so helper functions are available
I'm not sure how efficient this code is with really many items (BTW, you did not mention the order of magnitude of the numbers) , but I think it will be more efficient than converting arrays into Laravel collections
$arr = [
0 => [
"subscriber" => "gmail.com.",
"code" => 554,
"status" => 50,
],
1 => [
"subscriber" => "apied.be",
"code" => 550,
"status" => 51,
],
2 => [
"subscriber" => "beton-dobbelaere.be",
"code" => 550,
"status" => 50,
],
3 => [
"subscriber" => "live.be",
"code" => 550,
"status" => 51,
],
4 => [
"subscriber" => "hotmail.be",
"code" => 550,
"status" => 51,
],
5 => [
"subscriber" => "telenet.be",
"code" => 550,
"status" => 50,
],
6 => [
"subscriber" => "telenet.be",
"code" => 550,
"status" => 55,
],
7 => [
"subscriber" => "telenet.be",
"code" => 550,
"status" => 51,
],
8 => [
"subscriber" => "telenet.be",
"code" => 550,
"status" => 51,
],
];
$res = [];
foreach($arr as $element) {
if(empty($res[$element['subscriber']])) {
$res[$element['subscriber']] = [$element, 'count' => 1];
} else {
$res[$element['subscriber']]['count']++;
}
}
var_dump($res);
Try
<?php
$input = array(
0 => array(
"subscriber" => "gmail.com.",
"code" => 554,
"status" => 50),
1 => array(
"subscriber" => "apied.be",
"code" => 550,
"status" => 51),
2 => array(
"subscriber" => "beton-dobbelaere.be",
"code" => 550,
"status" => 50),
3 => array(
"subscriber" => "live.be",
"code" => 550,
"status" => 51),
4 => array(
"subscriber" => "hotmail.be",
"code" => 550,
"status" => 51),
5 => array(
"subscriber" => "telenet.be",
"code" => 550,
"status" => 50),
6 => array(
"subscriber" => "telenet.be",
"code" => 550,
"status" => 55),
7 => array(
"subscriber" => "telenet.be",
"code" => 550,
"status" => 51),
8 => array(
"subscriber" => "telenet.be",
"code" => 550,
"status" => 51)
);
/**
*#param array $counted The array already counted or NULL
*#param array $new The array to count or to merge with the counted $counted
*/
function merge_xor_count(array $counted = NULL, array $new){
if($counted === NULL){
$counted = array();
}
foreach($new as $keyNew => $valueNew){
$matches = false;
foreach($counted as $keyOut => $valueOut){
if ($valueOut['subscriber'] == $valueNew['subscriber'] && $valueOut['code'] == $valueNew['code'] &&
$valueOut['status'] == $valueNew['status']){
$matches = $keyOut;
}
}
if($matches !== false){
$counted[$matches]['amount']++;
}
else{
if(!isset($valueNew['amount'])) $valueNew['amount'] = 1;
$counted[] = $valueNew;
}
}
return $counted;
}
$output = merge_xor_count(NULL, $input);
print_r ($output)."\n";
$output = merge_xor_count($output, $input);
print_r ($output)."\n";
?>
I managed to fix it myself with a foreach loop it runs faster then i expected (0.3179 seconds)
$merged = [];
foreach($hardbounces as &$hardbounce){
if(empty($merged)){
$merged[] = $hardbounce;
}else{
$i = 0;
foreach($merged as $key => $merge){
$i++;
if($hardbounce['subscriber'] == $merge['subscriber'] && $hardbounce['code'] == $merge['code'] && $hardbounce['status'] == $merge['status']){
$merged[$key]['amount']++;
break;
}
if(count($merged) == $i){
$merged[] = $hardbounce;
}
}
}
}
I loop through the bounces, if merged is empty (first iteration) it just adds the hardbounce.
From that point on I will loop through the new array and check if there is a duplicate. When this happens we just add one to the amount and break out of the foreach. Otherwise it will eventually still add duplicates.
After i check if we made it to the last iteration, which means if it still hasn't break-ed we should add the hardbounce because it doesn't exist yet.
To be clear I made sure the amount 1 already exists before this loop runs, instead of adding it during this loop.
Related
Not able to group inner array of the multi-dimensional array
I have an array like this & I need to group an array based on value. array:3 [ 0 => array:5 [ "id" => 1 "maxView" => 0 "maxClick" => 20 "companyName" => "Project A" "email" => "user1#email.com" ] 1 => array:5 [ "id" => 1 "maxView" => 0 "maxClick" => 20 "companyName" => "Project A" "email" => "user2#email.com" ] 2 => array:5 [ "id" => 1 "maxView" => 0 "maxClick" => 20 "companyName" => "Project A" "email" => "user3#email.com" ] 3 => array:5 [ "id" => 1 "maxView" => 0 "maxClick" => 20 "companyName" => "Project B" "email" => "user1#email.com" ] 4 => array:5 [ "id" => 1 "maxView" => 0 "maxClick" => 20 "companyName" => "Project B" "email" => "user6#email.com" ] ] So far, I have following lines of codes. This is grouping my array based on fieldName = companyName But I am not able to add emails belonging to companyname under an array. foreach ($records as $record) { if (!array_key_exists($record[$fieldName], $groupedArray)) { $groupedArray[$record[$fieldName]] = []; } $groupedArray[$record[$fieldName]] = $record; $groupedArray[$record[$fieldName]]['email'] = []; if (!in_array($record['email'], $groupedArray[$record[$fieldName]]['email'], true)) { $groupedArray[$record[$fieldName]]['email'][] = $record['email']; } } My code is adding only last email to the array. "Project A" => [ .... "email" => array:1 [ 0 => "user3#email.com" ] Can anybody help me what I am missing here ?
Yii2 select2 selected one value 2 and more time
I have select2 return $this->form->field($this->model, 'observers') ->widget(Select2::className(), [ 'data' => Tasks::getAllStaffsGroupOffice(), 'disabled' => !$this->can['changeObservers'], 'options' => [ 'multiple' => true, 'value' => ArrayHelper::map($this->model->observers, 'staff_id', 'staff_id'), 'placeholder' => Yii::t('tasks_forms', 'FORM_PLACEHOLDER_CHOOSE'), 'class' => 'hiddenInput' ], 'pluginOptions' => [ 'allowClear' => true, 'closeOnSelect'=> false, ], 'pluginLoading' => false, ]); Tasks::getAllStaffsGroupOffice() geting users array by offices. Example -> array:4 [▼ "main office" => array:1 [▼ 2 => "123 123" ] "office 1" => array:3 [▼ 3 => "staff_1" 6 => "staff_2" 2 => "123 123" ] "office 3" => array:2 [▼ 4 => "staff_3" 3 => "staff_1" ] "office 2" => array:2 [▼ 5 => "staff_4" 3 => "staff_1" ] ] select2 value example -> array (2 => "2") As a result, the display of the widget itself looks like this select2 value How to make it so that the staff which is in 2 and more offices is displayed only 1 time?
I believe you should filter your staff list and then pass it to Select2 widget. My approach for creating a unique array of staff is like this: $allStaffsGroupOffice = [ "main office" => [ 2 => "123 123" ], "office 1" => [ 3 => "staff_1", 6 => "staff_2", 2 => "123 123" ], "office 3" => [ 4 => "staff_3", 3 => "staff_1" ], "office 2" => [ 5 => "staff_4", 3 => "staff_1" ] ]; $repeatedStaff = []; $newUniqueList = []; foreach ($allStaffsGroupOffice as $office => $staffList) { foreach ($staffList as $staffId => $staffName) { if (!in_array($staffId, $repeatedStaff)) { $repeatedStaff[] = $staffId; $newUniqueList[$office][$staffId] = $staffName; } } } $newUniqueList will be your new array which contains offices with unique staff.
Laravel Request has array but iteration error "Trying to get property 'product' of non-object"
When I used return $request->all(); network preview tab 0 => array:10 [ "id" => 1 "user_id" => 1 "product_id" => 1 "brand_id" => 1 "quantity" => 2 "total" => "90000" "created_at" => "2021-05-21T07:48:34.000000Z" "updated_at" => "2021-05-21T07:48:34.000000Z" "product" => array:10 [ "id" => 1 "name" => "S1 Pro" "category_id" => 1 "sub_category_id" => 1 "brand_id" => 1 "buy_price" => "40000.00" "sell_price" => "45000.00" "description" => "this is a new phone" "created_at" => "2021-05-20T06:02:28.000000Z" "updated_at" => "2021-05-20T06:02:28.000000Z" ] "brand" => array:4 [ "id" => 1 "name" => "Vevo" "created_at" => "2021-05-20T05:48:01.000000Z" "updated_at" => "2021-05-20T05:48:01.000000Z" ] ] 1 => array:10 [ "id" => 2 "user_id" => 1 "product_id" => 2 "brand_id" => 2 "quantity" => 1 "total" => "195000" "created_at" => "2021-05-21T08:49:47.000000Z" "updated_at" => "2021-05-21T08:49:47.000000Z" "product" => array:10 [ "id" => 2 "name" => "Note 20 ultra" "category_id" => 1 "sub_category_id" => 2 "brand_id" => 2 "buy_price" => "180000.00" "sell_price" => "195000.00" "description" => "A new phone a new Era" "created_at" => "2021-05-21T06:39:43.000000Z" "updated_at" => "2021-05-21T06:39:43.000000Z" ] "brand" => array:4 [ "id" => 2 "name" => "Samsung" "created_at" => "2021-05-21T06:37:54.000000Z" "updated_at" => "2021-05-21T06:37:54.000000Z" ] ] ] But when Im using foreach loop to access every item it give error of "Trying to get property 'product' of non-object" method I'm using public function sellProduct(Request $request){ // dd($request->all()); $cardData = $request->all(); foreach ($cardData as $value) { return $value->product; } } please help to resolve this issue, basically I'm creating a cart app through request I'm getting all the details and product
Access the product like $value['product'] since it's an array and not an object.
Get only unique item from collection and sum other values to one item
I do have this laravel collection Collection {#239 ▼ #items: array:2 [▼ 0 => array:21 [▼ "dueDate" => "2017-09-29" "groupByCode" => "REMINDER" "date" => "2017-09-29" "number" => "9030014" "status" => "Reminder" "currencyCode" => "kr" "amount" => 1745.0 "remainingAmount" => 1745.0 ], 2 => array:21 [▼ "dueDate" => "2017-09-29" "groupByCode" => "REMINDER" "date" => "2017-09-29" "number" => "9030014" "status" => "Reminder" "currencyCode" => "kr" "amount" => 1345.0 "remainingAmount" => 1745.0 ], 3 => array:21 [▼ "dueDate" => "2017-09-29" "groupByCode" => "INVOICE" "date" => "2017-09-29" "number" => "9030026" "status" => "invoice" "currencyCode" => "kr" "amount" => 2389.0 "remainingAmount" => 2389.0 ] ] } Now my question is how to get only unique items based on status, example above I have two items with status Reminder, instead I want to keep the same collection format but in return to have only one item with status 'Reminder' with all existing keys and the sum will be sum of both reminders.... I have tried to use `where('status', ''Reminder')->first() but does not work since it takes only firts element. So the output should be something along the lines of this: Collection {#239 ▼ #items: array:2 [▼ 0 => array:21 [▼ "dueDate" => "2017-09-29" "groupByCode" => "REMINDER" "date" => "2017-09-29" "number" => "9030014" "status" => "Reminder" "currencyCode" => "kr" "amount" => TOTAL OF BOTH REMINDERS "remainingAmount" => 1745.0 ], 2 => array:21 [▼ "dueDate" => "2017-09-29" "groupByCode" => "INVOICE" "date" => "2017-09-29" "number" => "9030014" "status" => "Invoice" "currencyCode" => "kr" "amount" => 2312 "remainingAmount" => 1745.0 ], } Thanks! Here is the code: $invoices = $this->boatService->getInvoices(cleanSsn(session('ssn'))); $invoices = collect($invoices)->whereIn('status', ['Reminder', 'Invoice']);
You can make a raw query to sum the items and group them by status. Try this: $collection = \DB::table('invoices') ->select(\DB::raw('sum(amount) as amount_total, status')) ->groupBy('status') ->get();
Laravel collection customize
Given array like this, I want to arrange data in a custom way. array:5 [ 0 => array:4 [ "message" => "Message number 1 for first conversation" "is_seen" => 0 "user_id" => 2 "sent_time" => "2017-08-30 23:28:50" ] 1 => array:4 [ "message" => "Message number 2 for first conversation" "is_seen" => 1 "user_id" => 1 "sent_time" => "2017-08-29 23:36:27" ] 2 => array:4 [ "message" => "Message number 3 for first conversation" "is_seen" => 1 "user_id" => 1 "sent_time" => "2017-08-29 23:36:27" ] 3 => array:4 [ "message" => "Message number 4 for first conversation by second user" "is_seen" => 1 "user_id" => 2 "sent_time" => "2017-08-29 23:36:27" ] 4 => array:4 [ "message" => "Message number 5 for first conversation by second user" "is_seen" => 1 "user_id" => 2 "sent_time" => "2017-08-29 23:36:27" ] ] What I want to do is to group messages that are sent by the same user so I get an array like this : $data = [ "block" => [ [ "message" => "Message number 1 for first conversation", "is_seen" => 0, "user_id" => 2, "sent_time" => "2017-08-30 23:28:50" ] ], "block" => [ [ "message" => "Message number 2 for first conversation", "is_seen" => 1, "user_id" => 1, "sent_time" => "2017-08-29 23:36:27" ], [ "message" => "Message number 3 for first conversation", "is_seen" => 1, "user_id" => 1, "sent_time" => "2017-08-29 23:36:27" ] ], "block" => [ [ "message" => "Message number 4 for first conversation by second user", "is_seen" => 1, "user_id" => 2, "sent_time" => "2017-08-29 23:36:27" ], [ "message" => "Message number 5 for first conversation by second user", "is_seen" => 1, "user_id" => 2, "sent_time" => "2017-08-29 23:36:27" ] ] ]; So basically, If next message user_id is same as previous message user_id add it to group array. If it is not, create new group array and so on. I do not want to group all user messages together since this structure is required for pagination in front-end.
You can do it like this $new = array(); $prev = ""; $count =0; foreach($arr as $key=> $value){ if($prev == $value["user_id"]){ $new[$count-1][] = $value; }else { $new[$count][] = $value; $count++; }$prev = $value["user_id"]; } print_r($new); Live demo : https://eval.in/854288
First of all you cannot have duplicate associative keys into the same array however, you group the blocks by some sequential number in the following way: $group_num = 0; $col = collect($data); $col->groupBy(function ($item, $key) use (&$group_num, $col) { if (!isset($col[$key - 1])) return "block_" . $group_num; if ($item['user_id'] !== $col[$key - 1]['user_id']) $group_num++; return "block_" . $group_num; }) ->toArray(); The result it should be something like: [ "block_0" => [ [ "message" => "Message number 1 for first conversation", "is_seen" => 0, "user_id" => 2, "sent_time" => "2017-08-30 23:28:50", ], ], "block_1" => [ [ "message" => "Message number 2 for first conversation", "is_seen" => 1, "user_id" => 1, "sent_time" => "2017-08-29 23:36:27", ], [ "message" => "Message number 3 for first conversation", "is_seen" => 1, "user_id" => 1, "sent_time" => "2017-08-29 23:36:27", ], ], "block_2" => [ [ "message" => "Message number 4 for first conversation by second user", "is_seen" => 1, "user_id" => 2, "sent_time" => "2017-08-29 23:36:27", ], [ "message" => "Message number 5 for first conversation by second user", "is_seen" => 1, "user_id" => 2, "sent_time" => "2017-08-29 23:36:27", ], ], ]