Append two arrays having same key inside one array differently - php

I have two array in php likes this :
$a = [ 1 => [ 0 => 10, 1 => 1 ] ] and $b = [ 1 => [ 0 => 15, 1 => 3 ] ]
I have to make a union of these two arrays that resultant array should be like this :
$r = [ 1 => [ 0 => 10, 1 => 1 ], 1 => [ 0 => 15, 1 => 3 ] ]
Please give me idea how can i achieve this ..
Thanks in advance !!

You can't give same array index....index is unique
$r[] = $a;
$r[] = $b;
so your array will be
$r = [ [0]=>[1 => [ 0 => 10, 1 => 1 ]], [1] =>[1 => [ 0 => 15, 1 => 3 ] ]]

You can't have an array with the same key.
If you want to merge values of two arrays, use array_merge:
$a = array(
0 => 10,
1 => 1 ) ;
print_r($a);
echo "<br>";
$b = array(0 => 15,
1 => 3);
print_r($b);
echo "<br>";
$result = array_merge($a, $b);
print_r($result);

Related

Sorting multiarray on numeric field

I have an array like below. I need to sort each array on the 2 key.
myarray [
[2020] = [0 => 123, 1 => 234, 2 => 45],
[2021] = [0 => 123, 1 => 34, 2 => 345],
[2019] = [0 => 123, 1 => 134, 2 => 645]
]
So the results would be
[2020] =
45
123
234
[2021] =
34
123
345
[2019] =
123
134
645]
I've tried
array_multisort( array_column($myarray, $myarray[2]), SORT_ASC, $myarray );
and
usort($myarray, array($myarray[2], 'sort_function'));
function sort_function($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
Neither of the above worked. Would someone please point out my mistake or the correct way to do this?
Here is a better explanation of the problem:
myarray [
[2020] = [0 => 123, 1 => 234, 2 => 357],
[2020] = [0 => 123, 1 => 34, 2 => 157],
[2020] = [0 => 123, 1 => 134, 2 => 257]
]
[2020] =
157
257
357
For your second result, use:
$second_elements = array_column($myarray, 2);
sort($second_elements);

Explode and transpose nominated comma-separated strings of an associative array

I have an array with comma-separated values like below:
array:5 [
"manufacturer" => "BB"
"width" => "245,225, ..."
"height" => "45,65, ..."
"diameter" => "19,17, ..."
"type" => "AA"
]
There is no limit to how many comma-separated values there may be, but all 3 of them will have same length.
From this, I want to get transposed output like below:
[
245,
45,
19,
],
[
45,
65,
17
]
So far, I have tried following code.
$mandatoryFields = [
'width',
'height',
'diameter',
];
$finalArray = [];
for ($i = 0; $i < 2; $i+=1) {
foreach ($mandatoryFields as $mandatoryField) {
$fieldArray = explode(',', $executionArray[$mandatoryField]);
$finalArray[] = [
$fieldArray[$i]
];
}
}
dd($finalArray);
But it is returning me:
array:6 [
0 => array:1 [
0 => "245"
]
1 => array:1 [
0 => "45"
]
2 => array:1 [
0 => "19"
]
3 => array:1 [
0 => "225"
]
4 => array:1 [
0 => "65"
]
5 => array:1 [
0 => "17"
]
]
The following logic might help you solve your issue:
<?php
$arr = [
"manufacturer" => "BB",
"width" => "245, 225",
"height" => "45, 65",
"diameter" => "19, 17",
"type" => "AA",
];
foreach ($arr as $key => $val) {
$split[$key] = explode(',', $val);
}
$mandatoryFields = [
'width',
'height',
'diameter',
];
// keep only mandatory fields
$split = array_intersect_key($split, array_flip($mandatoryFields));
$items = count($split[$mandatoryFields[0]]); // number of 'items' per mandatory field
for ($i = 0; $i < $items; $i++) {
$result[$i] = array_column($split, $i);
}
echo '<pre>';
print_r($result);
echo '</pre>';
Output:
Array
(
[0] => Array
(
[0] => 245
[1] => 45
[2] => 19
)
[1] => Array
(
[0] => 225
[1] => 65
[2] => 17
)
)
Essentially, this is an "explode & transpose" task. If the required columns are known, then they can be hardcoded like this: (Demo)
var_export(
array_map(
null,
explode(', ', $arr['width']),
explode(', ', $arr['height']),
explode(', ', $arr['diameter'])
)
);
Output:
array (
0 =>
array (
0 => '245',
1 => '45',
2 => '19',
),
1 =>
array (
0 => '225',
1 => '65',
2 => '17',
),
)
If the columns need to be dynamic, then map the required fields, explode on commas and unpack the generated rows to maintain a functional coding style: (Demo)
var_export(
array_map(
null,
...array_map(
fn($f) => explode(', ', $arr[$f]),
$mandatoryFields
)
)
);

How to insert items into existing array

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
)
)

PHP multidimensional array sort by value

Regarding this multidimensional array:
[
(int) 7 => [
(int) 0 => [
(int) 0 => '12:45',
(int) 1 => 'E1',
(int) 2 => 'B EXTREME 30'
],
(int) 1 => [
(int) 0 => '10:15',
(int) 1 => 'E1',
(int) 2 => 'B SHAPE 30'
],
],
(int) 1 => [
(int) 0 => [
(int) 0 => '09:30',
(int) 1 => 'E2',
(int) 2 => 'CYCLING VIRTUAL 50'
],
(int) 1 => [
(int) 0 => '10:30',
(int) 1 => 'E1',
(int) 2 => 'BODY PUMP VIRTUAL 60'
],
(int) 2 => [
(int) 0 => '11:45',
(int) 1 => 'E1',
(int) 2 => 'BODY BALANCE VIRTUAL 60'
],
],
(int) 2 => [
(int) 0 => [
(int) 0 => '14:45',
(int) 1 => 'E2',
(int) 2 => 'CYCLING VIRTUAL 50'
],
(int) 1 => [
(int) 0 => '17:00',
(int) 1 => 'E1',
(int) 2 => 'POSTURA ALONGAMENTO 60'
],
(int) 2 => [
(int) 0 => '09:15',
(int) 1 => 'E1',
(int) 2 => 'BODY PUMP 50'
],
]
]
The first key, of each first level array, are days of the week (day 7, day 1 and day 2).
The arrays inside each first level array contain hour (09:45), rooms (E1) and description (B EXTREME 30).
I tried to sort this multidimensional array by the second levels array hour value.
I used usort(), ksort(), array_multisort(), and some custom made functions for sorting the array as i need without luck.
The inside arrays must be sorted by ascending order, like this (example with day 2):
09:15 -> 14:45 -> 17:00
Does anyone knows how can i achieve this?
Assuming your data is called $data. Iterate the outer array, and apply a sort on each mid-level array, based on the time-part (in the innermost arrays). As your times are always formatted as "hh:ss", a string comparison in the usort callback does the job:
foreach ($data as &$events) {
usort($events, function($a, $b) {
return strcmp($a[0], $b[0]);
});
}
Note the & in the foreach: this makes sure you sort the original data, and not a copy of it.
If you want to create a new array, let's say $result, then do this (no & here!):
foreach ($data as $day => $events) {
usort($events, function($a, $b) {
return strcmp($a[0], $b[0]);
});
$result[$day] = $events;
}
usort — Sort an array by values using a user-defined comparison function.
Lets create a function where we compare time of two events.
This is part of your array
$array = [
2 => [
0 => [
0 => '14:45',
1 => 'E2',
2 => 'CYCLING VIRTUAL 50'
],
1 => [
0 => '17:00',
1 => 'E1',
2 => 'BODY PUMP VIRTUAL 60'
],
2 => [
0 => '09:15',
1 => 'E1',
2 => 'BODY BALANCE VIRTUAL 60'
],
],
];
This is an example how to sort items for one day (day 2)
usort($array[2], function ($a, $b) {
$time_a = strtotime($a[0]); // convert string to a unix timestamp to compare int
$time_b = strtotime($b[0]);
return $time_a - $time_b;
});
Output using print_r($array);
Array
(
[2] => Array
(
[0] => Array
(
[0] => 09:15
[1] => E1
[2] => BODY BALANCE VIRTUAL 60
)
[1] => Array
(
[0] => 14:45
[1] => E2
[2] => CYCLING VIRTUAL 50
)
[2] => Array
(
[0] => 17:00
[1] => E1
[2] => BODY PUMP VIRTUAL 60
)
)
)
To sort all days we do in a loop, passing each day array as a reference using &:
foreach ($array as &$day) {
usort($day, function ($a, $b) {
$time_a = strtotime($a[0]);
$time_b = strtotime($b[0]);
return $time_a - $time_b;
});
}

Restructuring a PHP multidimensional array that uses array_count_values and array_map

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'];
});

Categories