Assume I have two arrays as below
array:2 [
0 => {#1995
+"id": 6
+"sales_target_amount": "30000.00"
}
1 => {#1996
+"id": 10
+"sales_target_amount": "1000.00"
}
]
second array
array:2 [
0 => {#1994
+"sales_total": "4165.80"
+"staff_id": 6
}
1 => {#1513
+"sales_total": "1335.60"
+"staff_id": 10
}
]
I'm trying to insert the first array sales_target_amount into the second array if the id is matched to the staff_id.
code
$sum = array_merge($second_array[],$firs_array);
The code above will merge the two array together but not insert the items based on the id.
Expected Result
array:2 [
0 => {#1994
+"sales_total": "4165.80"
+"staff_id": 6
+"sales_target_amount": "30000.00"
}
1 => {#1513
+"sales_total": "1335.60"
+"staff_id": 10
+"sales_target_amount": "1000.00"
}
]
Here is the snippet, please see inline doc for explanation
$arr = [
0 => ["sales_total" => "4165.80", "staff_id" => 6],
1 => ["sales_total" => "1335.60", "staff_id" => 10],
];
$arr1 = [
0 => ["id" => 6, "sales_target_amount" => "30000.00"],
1 => ["id" => 10, "sales_target_amount" => "1000.00"],
];
//NULL to return complete arrays or objects and staff id will be key
$arr = array_column($arr, null, 'staff_id');
// mapping id sales target amount combination
$temp = array_column($arr1, 'sales_target_amount', 'id');
foreach ($arr as $key => $value) {
$arr[$key]['sales_target_amount'] = $temp[$key]; // putting sales_target_amount for id
}
array_column — Return the values from a single column in the input array
Demo Link.
Use hashed map to group values by staff_id
<?php
$arr1 = [
[
'id' => 6,
'sales_target_amount' => '30000.00'
],
[
'id' => 10,
'sales_target_amount' => '1000.00'
]
];
$arr2 = [
[
'sales_total' => '4165.80',
'staff_id' => 6
],
[
'sales_total' => '1335.60',
'staff_id' => 10
]
];
$results = [];
foreach ($arr1 as $value) {
$results[$value['id']] = [
'sales_target_amount' => $value['sales_target_amount']
];
}
foreach ($arr2 as $value) {
$results[$value['staff_id']] = array_merge($results[$value['staff_id']], $value);
}
print_r(array_values($results));
array_merge() do not work with multidimensional array.
Try to use array_merge_recursive().
And you not need use square brackets in this context.
Run function just like
$sum = array_merge_recursive($second_array, $firs_array);
Sort answer using this library
$array = array_merge_recursive(
Arr::flattenSingle(Arr::group($array1, 'id')),
Arr::flattenSingle(Arr::group($array2, 'staff_id'))
);
What it does underneath is to first group your arrays by column you want them merged by. Then because every element in group will have only one corresponding array (cause they are grouped by unique id) flatten those groups to build array with elements like id => array_with_that_id. And then merge those flattened groups by keys which in these case are ids.
Overall this will produce
Array (
[6] => Array (
[id] => 6
[sales_total] => 4165.8
[staff_id] => 6
[sales_target_amount] => 30000
)
[10] => Array (
[id] => 10
[sales_total] => 1335.6
[staff_id] => 10
[sales_target_amount] => 1000
)
)
Related
I have an array with vehicle_code and year and vehicle_code repeating for the same year in array. I want to remove if vehicle code and year values repeat in an array. I tried many array function like in_array array_column array_search so on. but all example show how to remove duplicate in 1 column only but I need to compare values for two columns in each set.
0 => array:2 [
0 => "AUA3H147"
1 => 2015
2 => Audi
3 => 12457
]
1 => array:2 [
0 => "AUA3H147"
1 => 2015
2 => tata
3 => 16832545
]
2 => array:2 [
0 => "AUA3H148"
1 => 2016
2 => tata
3 => 55555
]
3 => array:2 [
0 => "AUA3H148"
1 => 2017
2 => Audi
3 => 55555
]
I need output like the below:
0 => array:2 [
0 => "AUA3H147"
1 => 2015
2 => Audi
3 => 12457
]
1 => array:2 [
0 => "AUA3H148"
1 => 2016
2 => tata
3 => 16832545
]
2 => array:2 [
0 => "AUA3H148"
1 => 2017
2 => tata
3 => 55555
]
I tried like
$newArray = array();
$addArrayValues = array();
$priceVehicleYearCounter = 0
foreach ( $excelPriceArr as $key => $line ) {
if ( (!in_array($line[0], $addArrayValues)) && (!in_array($line[1], $addArrayValues)) ) {
$addArrayValues[$priceVehicleYearCounter] = array(
'id' => $priceVehicleYearCounter,
'vehicle_code' => $line[0],
'year' => $line[1],
);
$priceVehicleYearCounter++;
$newArray[$priceVehicleYearCounter] = $line;
}
}
something like this I saw same example with single column search i modified it to search two values but it didn't work
if(array_search($line, array_column($addArrayValues, 'vehicle_code','year')) == false) {
$addArrayValues[$priceVehicleYearCounter] = array(
'id' => $priceVehicleYearCounter,
'vehicle_code' => $line[0],
'year' => $line[1],
);
$priceVehicleYearCounter++;
$newArray[$priceVehicleYearCounter] = $line;
}
also like this:
$result = array_unique($addArrayValues, SORT_REGULAR);
$result = array_map("unserialize", array_unique(array_map("serialize", $addArrayValues)));
sample set in array:
1 => array:12 [
0 => "AUA3H142"
1 => 2013
2 => "AUDI"
3 => "A3 SPORTBACK"
4 => "1.8 TFSI AMBITION (01/2011-04/2013)"
5 => "1.8 TFSI AMBITION"
6 => 2209
7 => 500
8 => 12023
9 => 125648
10 => 118835
11 => "Private"
]
Use a composite first-level key when populating the result array. The key must be the combination of the values from the two identifying columns.
When finished iterating, re-index the array with array_values().
This is more generally demonstrated here: Filter/Remove rows where column value is found more than once in a multidimensional array
The null coalescing assignment operator will only store the first occurring data for each composite key.
Code: (Demo)
foreach ($array as $row) {
$compositeKey = $row[0] . '_' . $row[1];
$result[$compositeKey] ??= $row; // only store if first occurrence of compositeKey
}
var_export(array_values($result)); // re-index and print
I am trying to sort an array into groups and people.
the problem is that it only shows me one person per class when I use the counter "pos_group = 0" but if I put it in negative and increase it in the "IF" if I get the result.
Am I doing something else in the assignment of the new array?
<?php
$data = [
0 => [
'g_id' => 22,
'g_name' => 'ABC',
'u_id' => 1,
'u_name'=> 'Pepe'
],
1 => [
'g_id' => 22,
'g_name' => 'ABC',
'u_id' => 2,
'u_name'=> 'Mario'
],
2 => [
'g_id' => 22,
'g_name' => 'ABC',
'u_id' => null,
'u_name'=> null
],
3 => [
'g_id' => 31,
'g_name' => 'CDE',
'u_id' => 3,
'u_name'=> 'Juan'
],
4 => [
'g_id' => 41,
'g_name' => 'EFG',
'u_id' => 4,
'u_name'=> 'Pedro'
],
];
$last_group_id = null;
$pos_group = 0;
foreach ($data as $group_user):
//Add Groups
if($last_group_id != $group_user['g_id']){
//$pos_group ++;
$final[$pos_group] = [
'g_id' => $group_user['g_id'],
'g_name' => $group_user['g_name']
];
//$last_group_id = $group_user['g_id'];
//$pos_group ++;
}
//Add Members
if(!is_null($group_user['u_id'])){
$final[$pos_group]['members'][$group_user['u_id']] = [
'u_id' => $group_user['u_id'],
'u_name' => $group_user['u_name']
];
}
if($last_group_id != $group_user['g_id']){
$pos_group ++;
$last_group_id = $group_user['g_id'];
}
endforeach;
echo "<pre>";
print_r($final);
//var_dump($final);
echo "</pre>";
?>
expected:
Array
(
[0] => Array
(
[g_id] => 22
[g_name] => ABC
[members] => Array
(
[1] => Array
(
[u_id] => 1
[u_name] => Pepe
)
[2] => Array
(
[u_id] => 2
[u_name] => Mario
)
)
)
[1] => Array
(
[g_id] => 31
[g_name] => CDE
[members] => Array
(
[3] => Array
(
[u_id] => 3
[u_name] => Juan
)
)
)
[2] => Array
(
[g_id] => 41
[g_name] => EFG
[members] => Array
(
[4] => Array
(
[u_id] => 4
[u_name] => Pedro
)
)
)
)
I only get that result by setting the variable to -1 and increasing the counter after the "if", like this:
$last_group_id = null;
$pos_group = -1;
foreach ($data as $group_user):
//Add Groups
if($last_group_id != $group_user['g_id']){
$pos_group ++;
$final[$pos_group] = [
'g_id' => $group_user['g_id'],
'g_name' => $group_user['g_name']
];
$last_group_id = $group_user['g_id'];
//$pos_group ++;
}
//Add Members
if(!is_null($group_user['u_id'])){
$final[$pos_group]['members'][$group_user['u_id']] = [
'u_id' => $group_user['u_id'],
'u_name' => $group_user['u_name']
];
}
// if($last_group_id != $group_user['g_id']){
// $pos_group ++;
// $last_group_id = $group_user['g_id'];
// }
endforeach;
It's a bit silly but I can't find the problem XD
thanks !
It has to do with the position of your counter increment. The first case increments at the end of the loop, while the second one does it early in the loop. Let's observe what happens in the first case (incrementing at the end):
first iteration
$pos_group is 0
a new group is added (with id 22) at index 0
a new member is added inside that group (Pepe)
$pos_group is incremented to 1
second iteration
$pos_group is 1
group is not added as it is the same as the previous one
a new member is added (Mario), but at index 1, which places it outside the group (because the first member was added while $pos_group was 0)
$pos_group is not incremented
third iteration
here no changes are made, group is the same and id is null
fourth iteration
$pos_group is still 1 (no increment in last two iterations)
a new group is added at index 1, overwriting poor Mario
...algorithm continues normally until the end...
So the discrepancy happens because the first two members of the group are added at different indices. Incrementing the $pos_group variable at the beginning, before any additions, removes the problem, thus giving correct result.
But I'd like to suggest a simplified algorithm for this purpose:
$final = [];
foreach ($data as $groupUser) {
if (null !== $groupUser['u_id']) {
/* Note that for the final array I use the group id as key. That way
instead of tracking the current position and last group, you can simply
add at the group id index. I am also overwriting the group info every time
instead of checking if it exists (for simplicity), becuase there is no harm
done since it will always be the same. */
$final[$groupUser['g_id']]['g_id'] = $groupUser['g_id'];
$final[$groupUser['g_id']]['g_name'] = $groupUser['g_name'];
// add member, we've already checked the id against null
$final[$groupUser['g_id']]['members'][$groupUser['u_id']] = [
'u_id' => $groupUser['u_id'],
'u_name' => $groupUser['u_name']
];
}
}
No multiple checks, no additional variables to track, with the added benefit of working on unsorted data as well. The only difference is that my solution will have top level keys equal to group ids:
Array
(
[22] => Array...
[31] => Array...
If this is in any way a problem for you, all you have to do is grab array_values($final) to get a reindexed array, starting at 0.
I want to remove only key from multidimensional associative array preserving its value in PHP.
I've tried with foreach loop.
foreach($collections as $k=>$v){
foreach($v as $k1=>$v1){
foreach($v1 as $k2=>$v2){
if($k2 == 'attr_id'){
unset($collections[$k][$k1][$k2]);
}
}
}
}
I've an array like below:
$collections = [
0 => [
0 => [
"attr_id" => 23,
"value" => "Single Side"
],
1 => [
"attr_id" => 23,
"value" => "Double Side"
],
],
1 => [
0 => [
"attr_id" => 31,
"value" => "A4"
],
1 => [
"attr_id" => 31,
"value" => "12x18"
],
2 => [
"attr_id" => 31,
"value" => "B5"
],
3 => [
"attr_id" => 31,
"value" => "A5"
]
]
];
And I want output Like this:
$collections = [
23 => [ "Single Side", "Double Side" ],
31 => [ "A4", "12x18", "B5", "A5" ]
];
Please Help!
Short solution:
$expected = [];
foreach ($collections as $collection) {
$expected[$collection[0]['attr_id']] = array_column($collection, 'value');
}
You can do this with simply using two foreach() loop and push the value to attr_id
$expected = [];
foreach($collections as $k=>$v){
foreach($v as $k1=>$v1){
$expected[$v1['attr_id']][] = $v1['value'];
}
}
print_r($expected);
Output:
Array (
[23] => Array (
[0] => Single Side
[1] => Double Side
)
[31] => Array (
[0] => A4
[1] => 12x18
[2] => B5
[3] => A5
)
)
DEMO: https://3v4l.org/JlsIl
Do with array_values and array_reduce
first merge all subArray's to one array
The use reduce to apppend value based with attr_id matching
Sandbox demo
$res = call_user_func_array('array_merge',array_values($arr));
$res = array_reduce($res,function($a,$b){
$a[$b['attr_id']] = isset($a[$b['attr_id']]) ? $a[$b['attr_id']] : [] ;
array_push($a[$b['attr_id']],$b['value']);
return $a;
},[]);
print_r($res);
Can someone help me with converting a php array in a grouped format? I am trying to group them by id. I would like to have the following array converted:
$Arr1=Array
(
0 => Array
(
"id" => "4123",
"test_number" => "1",
"sat_total" => "1050"
),
1 => Array
(
"id" => "4123",
"test_number" => "2",
"sat_total" => "1130"
),
2 => Array
(
"id" => "4123",
"test_number" => "3",
"sat_total" => "1120"
),
3 => Array
(
"id" => "5555",
"test_number" => "1",
"sat_total" => "1130"
),
4 => Array
(
"id" => "5555",
"test_number" => "2",
"sat_total" => "1160"
)
);
into this:
$Arr2=Array
(
0 => Array
(
"id" => "4123",
"Score1" => "1050",
"Score2" => "1130",
"Score3" => "1120"
),
1 => Array
(
"id" => "5555",
"Score1" => "1130",
"Score2" => "1160"
)
);
I have tried a little bit, but can't seem to find how to make it work.
You only need to iterate your rows of data, determine if each row is the first occurring id value or not, then either declare the initial values, or add a variably keyed element to the group. When the loop finishes, call array_values() to reindex the array (remove the temporary keys).
Code: (Demo)
$Arr1=[
["id" => "4123", "test_number" => "1", "sat_total" => "1050"],
["id" => "4123", "test_number" => "2", "sat_total" => "1130"],
["id" => "4123", "test_number" => "3", "sat_total" => "1120"],
["id" => "5555", "test_number" => "1", "sat_total" => "1130"],
["id" => "5555", "test_number" => "2", "sat_total" => "1160"]
];
foreach ($Arr1 as $set) {
if (!isset($result[$set['id']])) {
$result[$set['id']] = ['id' => $set['id'], 'Score1' => $set['sat_total']];
} else {
$result[$set['id']]['Score' . sizeof($result[$set['id']])] = $set['sat_total'];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => '4123',
'Score1' => '1050',
'Score2' => '1130',
'Score3' => '1120',
),
1 =>
array (
'id' => '5555',
'Score1' => '1130',
'Score2' => '1160',
),
)
This method will find the scores matching the $id.
It uses three array_intersects to match all the values correct.
This method will only loop the number of unique IDs, in your case two times.
Plus the times to create the score keys.
I do agree with what ggorlen says about the keys. That will also create a more efficient code.
$ids = array_column($Arr1, "id");
$sat = array_column($Arr1, "sat_total");
foreach(array_unique($ids) as $id){
$new[$id] = ["id" => $id];
$tmp = array_values(array_intersect_key($sat,array_intersect_key($Arr1, array_intersect($ids, [$id]))));
for($i=1;$i<=count($tmp);$i++) $new[$id]["Score" . $i] = $tmp[$i-1];
}
var_dump($new);
https://3v4l.org/ag3To
The output is an associative array with id as key.
You can use array_values if you want to make it indexed.
Just to show how much more efficient the code can be with one score array.
This is what it would look like:
$ids = array_column($Arr1, "id");
$sat = array_column($Arr1, "sat_total");
foreach(array_unique($ids) as $id){
$new[] = ["id" => $id, "scores" => array_values(array_intersect_key($sat,array_intersect_key($Arr1, array_intersect($ids, [$id]))))];
}
var_dump($new);
https://3v4l.org/mdA0W
$arr2 = [];
$i = 0;
$length = count($arr1);
do {
$builder = $arr1[$i];
// grab the first item from the original array
$builder = [
// set its initial properties
'id' => $arr1[$i]['id'],
'Score1' => $arr1[$i]['sat_total'],
];
// initialise the subsequent score number
$testNumber = 2;
// prepare to look ahead in the original array for a matching id
while (($i + 1) < $length) { // only look ahead if it is possible to
if ($arr1[$i + 1]['id'] == $builder['id']) {
// did you find a matching id? if so, let's set the subsequent score
$builder["Score$testNumber"] = $arr1[$i + 1]['sat_total'];
$testNumber++; // increase the next score number
$i++; // increase the original array index
} else {
// no luck? let's go forwards and set the next record
break;
}
}
$arr2[] = $builder; // set the built record into the new array
$i++; // move the pointer forwards
} while ($i < $length); // as long as there are items ahead
Not often you get to use a do-while. But it works :)
Feed it your original array $arr1 and $arr2 will be set.
It works by looking forward for matching ids. This solution assumes your original array is ordered by id! So unless you trust the input - don't use this solution!
Otherwise this is a simple, fast, and fairly readable solution to what looks to me like a school exercise?
If you want something that is safe, the other solutions here are suitable.
I'm not sure this structure is ideal--it seems like your keys "Score1", "Score2" etc would be best as an array like scores => [1050, 1130, ...] and it feels like the ids should be keys in the result array. But in any case, this gives your requested output:
$res = [];
foreach ($arr as $e) {
if (!array_key_exists($e['id'], $res)) {
$res[$e['id']] = [];
}
$res[$e['id']]["Score".(count($res[$e['id']])+1)] = $e['sat_total'];
}
$count = 0;
foreach ($res as $k => $v) {
$res[$k]['id'] = $k;
$res[$count++] = $res[$k];
unset($res[$k]);
}
print_r($res);
Output
Array
(
[0] => Array
(
[Score1] => 1050
[Score2] => 1130
[Score3] => 1120
[id] => 4123
)
[1] => Array
(
[Score1] => 1130
[Score2] => 1160
[id] => 5555
)
)
Note that I did two passes which is a little verbose, but taking the time to key in the data ids into the array in the first pass should improve a linear search through the array for each element into O(1) hashing, so I think it's worth the extra loop block.
Using Laravel 5.6, I am creating a multidimensional array that looks like this:
array:8 [
0 => array:2 [
0 => "ELA-2"
1 => 7
]
1 => array:2 [
0 => "Science-3"
1 => 9
]
2 => array:2 [
0 => "ELA-1"
1 => 5
]
3 => array:2 [
0 => "Science-2"
1 => 9
]
4 => array:2 [
0 => "ELA-4"
1 => 2
]
5 => array:2 [
0 => "ELA-3"
1 => 7
]
6 => array:2 [
0 => "Science-4"
1 => 2
]
7 => array:2 [
0 => "Science-1"
1 => 1
]
]
This was created using array_count_values and array_map (if that matters).
What I want is the data to look like this:
array:8 [
0 => array:3 [
"Subject" => "ELA"
"Level" => 2
"Count" => 7
]
1 => array:3 [
"Subject" => "Science"
"Level" => 3
"Count" => 9
]
2 => array:3 [
"Subject" => "ELA"
"Level" => 1
"Count" => 5
]
3 => array:3 [
"Subject" => "Science"
"Level" => 2
"Count" => 9
]
4 => array:3 [
"Subject" => "ELA"
"Level" => 4
"Count" => 2
]
5 => array:3 [
"Subject" => "ELA"
"Level" => 3
"Count" => 7
]
6 => array:3 [
"Subject" => "Science"
"Level" => 4
"Count" => 2
]
7 => array:3 [
"Subject" => "Science"
"Level" => 1
"Count" => 1
]
]
I am not sure how to:
break apart the values from key[0] within each array and separate them into 2 parts—the first portion for the 'Subject' and the second portion for the 'Level'
relabel the numeric incrementing keys (there will be 3 within each array) to the words "Subject", "Level", and "Count", respectively.
My current code looks like this:
// create an empty array
$array_holder = [];
// loop through each student
foreach($class_students as $student) {
// loop through each subject
foreach($class_subjects as $subject) {
// count each child at his/her level (rounded average)
$childs_level = AssessmentData::where('student_id', $student->id)->where('subject_id', $subject->subject_id)->avg('assessed_level');
// get the subject name
$current_subject = Subject::where('id', $subject->subject_id)->first();
// round the average
$childs_level = round($childs_level);
// convert the childs_level into a whole number
$childs_level = number_format($childs_level, 0);
// add each child by appending to an array
if ($childs_level != 0) {
$compiled_array[] = array_push($array_holder, $current_subject->short_name."-".$childs_level);
}
}
}
// count each assessed_level from the resultant array
$counted_array = array_count_values($array_holder);
// remap the array
$counted_array = array_map(null, array_keys($counted_array), $counted_array);
Additional request - sorting
I would like the results to be sorted by count so that Level 1 is first, Level 2 is second, Level 3 is third, and Level 4 is fourth. (Currently, it is sorted in the opposite order - Level 4 is first, Level 3 is second, etc.)
You have to loop the original array and create a new one:
<?php
$original_array = [
['ELA-2', 7],
['Science-3', 9],
['ELA-1', 5]
// you can fill in the rest
];
$new_array = [];
foreach($original_array as $v)
{
$new_array[] = [
'Subject' => explode('-', $v[0])[0], // split the subject on "-" and retrieve the first item
'Level' => explode('-', $v[0])[1], // split the subject on "-" and retrieve the second item
'Count' => $v[1] // this is given to us so just use it
];
}
// Sort the finished product in ascending order based on the "count" of the sub-arrays
//
// You can also use this earlier on $original_array if you want
// but you would just change the arithmetic to $a[ 1 ] - $b [ 1 ]
usort( $new_array, function( $a, $b ){
return $a[ 'Count' ] - $b[ 'Count' ];
} );
print_r($new_array);
Output:
Array
(
[0] => Array
(
[Subject] => ELA
[Level] => 1
[Count] => 5
)
[1] => Array
(
[Subject] => ELA
[Level] => 2
[Count] => 7
)
[2] => Array
(
[Subject] => Science
[Level] => 3
[Count] => 9
)
)
If you wanted to get fancy and array_map() it then this is achieves the same result as foreach(){}:
$new_array = array_map(
function($v){
return [
'Subject' => explode('-', $v[0])[0], // split the subject on "-" and retrieve the first item
'Level' => explode('-', $v[0])[1], // split the subject on "-" and retrieve the second item
'Count' => $v[1] // this is given to us so just use it
];
},
$original_array
);
I want to add an alternative approach.
Important notes:
don't ask PHP to perform the same explode() call on the same string twice -- cache it as a temporary variable, or as shown in my snippet use all generated elements at once.
limit your explode() call by declaring the limit parameter (3rd param) so that the output is consistent and the script is clear about the intended action (seeing the input data is not required to understand the intention).
the spaceship operator in usort() is very versatile and will allow you to make a variety of comparisons without changing syntax.
Code: (Demo)
$counted_array = [
["ELA-2", 7],
["Science-3", 9],
["ELA-1", 5],
["Science-2", 9],
["ELA-4", 2],
["ELA-3", 7],
["Science-4", 2],
["Science-1", 1]
];
$assoc_array = array_map(
function($subarray){
return array_combine(
['Subject', 'Level'], explode('-', $subarray[0], 2)
)
+ ['Count' => $subarray[1]];
},
$counted_array
);
usort($assoc_array, function($a, $b) {
return [$a['Subject'], $a['Level']]
<=>
[$b['Subject'], $b['Level']];
});
var_export($assoc_array);
I noticed in your question, you asked for the data to be sorted on Count, but then proceeded to explain how you expected the sorting by Level -- this is ambiguous.
If you want to sort on Count, just use:
usort($assoc_array, function($a, $b) {
return $a['Count'] <=> $b['Count'];
});