How to add multiple array elements into a transposed structure? - php

I have these arrays
$months = ['jan', 'feb'];
$cashUniform = [2000, 1200];
$cashFee = [24000, 34000];
$cashExpenses = [4000, 300];
Am trying to create an object from these arrays like so:
$data = [{
'month': 'jan',
'cashUniform': 2000,
'cashFee': 24000,
'cashExpenses': 4000,
},
{
'month': 'feb',
'cashUniform': 12000,
'cashFee': 34000,
'cashExpenses': 300,
}
];
I've tried array_combine but it only accepts two array elements, and in my case, I have four array elements.
I've also tried to to create a multiple array.
$data['months'] = $months;
$data['cashFee'] = $cashFee;
$data['cashUniform'] = $cashUniform;
$data['cashExpenses'] = $cashExpenses;
dd(json_encode($data));
The code above returns
{"months":["JAN","FEB"],"cashFee": [12500,2000],"cashUniform":[2000,0],"cashExpenses":[1500,0]}

You can use a simple for loop to loop over all arrays and add the values to a single array:
$data = [];
for ($i = 0; $i < count($months); $i++) {
$data[] = [
'month' => $monts[$i],
'cashUniform' => $cashUniform[$i],
'cashFee' => $cashFee[$i],
'cashExpenses' => $cashExpenses[$i],
];
}
dd($data);
This does require that all arrays have the same amount of values!

$data = [];
foreach ($months as $index => $value) {
$data[] = [
'month' => $monts[$index],
'cashUniform' => $cashUniform[$index],
'cashFee' => $cashFee[$index],
'cashExpenses' => $cashExpenses[$index],
];
}
dd($data);

How about some elegant functional programming so that you don't need to declare a global result array? Transpose the individually declared input data using array_map() -- this synchronously iterates each array. Then name the custom function's parameters to align with the desired keys in the output's subarrays. Use compact() to avoid manually re-writing the keys and variables in the return line.
Code: (Demo)
$months = ['jan', 'feb'];
$cashUniform = [2000, 1200];
$cashFee = [24000, 34000];
$cashExpenses = [4000, 300];
var_export(
array_map(
function ($month, $cashUniform, $cashFee, $cashExpenses) {
return compact(['month', 'cashUniform', 'cashFee', 'cashExpenses']);
},
$months,
$cashUniform,
$cashFee,
$cashExpenses
)
);
Output:
array (
0 =>
array (
'month' => 'jan',
'cashUniform' => 2000,
'cashFee' => 24000,
'cashExpenses' => 4000,
),
1 =>
array (
'month' => 'feb',
'cashUniform' => 1200,
'cashFee' => 34000,
'cashExpenses' => 300,
),
)

Related

How to remove a array from an array by comparing it with another array?

I've 2 array as below. One array is a 2 dimensional array($array_1) and another is a simple array ($array_2). This $array_1 as a key called private_name in each array and $array_2 has a list of private_key values. I want to keep the array from $array_1 which matches with $array_2.
$array_1 = [
[0] => ['id'=>12, 'private_name' => 'name12', 'age' => '23'],
[1] => ['id'=>2, 'private_name' => 'name2', 'age' => '23'],
[2] => ['id'=>9, 'private_name' => 'name1', 'age' => '23'],
[3] => ['id'=>11, 'private_name' => 'name11', 'age' => '23'],
.
.
.
[999] => ['id'=>999, 'private_name' => 'name999', 'age' => '23'],
];
$array_2 = ['name1', 'name2', 'name3',....];
So i wante remove array contents from $array_1 which matches with $array_2. Currently am using the below method but it takes a lot of time as there are 14k+ array values in $array_1. Is there any soulution for this which just uses 1 line to solve the above. I want a solution like
$newVal = array_intersect(array_column($array_1, 'private_name'), $array_2);
Current am doing like below which takes a lot of time
$results = array();
$count = 0;
if (count($array_1) > 0) {
foreach ($array_1 as $row) {
foreach ($row as $col => $val) {
foreach ($array_2 as $key3 => $pvt_name) {
if (strcmp($row['private_name'], $array_1) == 0) {
$results[$count][$col] = $val;
}
}
}
$count++;
}
}
Any help is much appreciated. Thank you
This can easily be done using array_filter.
$filtered_array = array_filter($array_1, function($item) use($array_2) {
return !in_array($item['private_name'], $array_2);
});
var_dump($filtered_array);
The callback function needs to return true to keep the item in the result, false to discard it. That is done here by checking if the private_name value of the item is contained in your second array - and the result of that negated, because you only want to keep those that don’t match.

Remove array items based on sub array duplicate values and then sort in custom order

Remove array items based on duplicate values that appear 1 level deeper inside the array. Once the items have been sorted of duplicates, then would be cool to re order the new array.
This is the current input array...
$downloads = [
['type' => 'PHOTOS'],
['type' => 'DOCUMENTS'],
['type' => 'DOCUMENTS'],
['type' => 'VIDEOS'],
['type' => 'PHOTOS'],
];
I would like to remove all duplicates from this input so I am left with this new output...
[
['type' => 'PHOTOS'],
['type' => 'DOCUMENTS'],
['type' => 'VIDEOS'],
]
But is it possible to set and ordering to each TYPE value. For example can I set predetermined orders using a variables or something. Any advice on re-ordering the new array to a specific order. Using this new order...
$photos = 1;
$videos = 2;
$documents = 3;
or a new order using an array maybe...
$new_order = array(
1 => 'PHOTOS',
2 => 'VIDEOS',
3 => 'DOCUMENTS'
)
Any help would be so good. I've tried array_unique and array_map but I can't seem to find out how to specify which sub array key to check for duplicates.
This is what i've tried so far...
$downloads = get_field('downloads');
$types = array_unique($downloads));
and
$downloads = get_field('downloads');
$types = array_map("unserialize", array_unique(array_map("serialize", $downloads)));
I didn't get as far as re ordering the array.
The solution using array_column, array_map, array_search and usort functions:
$new_order = array(
0 => 'PHOTOS',
1 => 'VIDEOS',
2 => 'DOCUMENTS'
);
// $downloads is your input array
$types = array_map(function ($v) {
return ['TYPE' => $v];
},array_unique(array_column($downloads, 'TYPE')));
usort($types, function($a, $b) use($new_order){
$a_key = array_search($a['TYPE'], $new_order);
$b_key = array_search($b['TYPE'], $new_order);
if ($a_key == $b_key) return 0;
return ($a_key < $b_key)? -1 : 1;
});
print_r($types);
The output:
Array
(
[0] => Array
(
[TYPE] => PHOTOS
)
[1] => Array
(
[TYPE] => VIDEOS
)
[2] => Array
(
[TYPE] => DOCUMENTS
)
)
Quick and dirty solution for removing "subarray" duplicates:
$a = [];
$a[]['type'] = 'photos';
$a[]['type'] = 'documents';
$a[]['type'] = 'documents';
$a[]['type'] = 'videos';
$a[]['type'] = 'photos';
$b = [];
foreach( $a as $index => $subA ) {
if( in_array($subA['type'], $b) ) {
unset($a[$index]);
} else {
$b[] = $subA['type'];
}
}
Regarding the sorting: Just set the indices with the order you need them and after removing the duplicates use ksort (assuming I did understand you right).
Simply use array_intersect that computes common part of two arrays. If you use order array as first argument, order of that will be preserved.
Your input:
$input = [
['TYPE' => 'PHOTOS'],
['TYPE' => 'DOCUMENTS'],
['TYPE' => 'DOCUMENTS'],
['TYPE' => 'VIDEOS'],
['TYPE' => 'PHOTOS']
];
I've write simple function to achieve all of your needs:
function do_awesomness($input, $key = 'TYPE', $order = ['PHOTOS', 'VIDEOS', 'DOCUMENTS'])
{
$result = [];
foreach($input as $k => $value)
$result[] = $value[$key];
$result = array_unique($result);
return array_values(array_intersect($order, $result));
}
Usage:
do_awesomness($input);
Working example: http://phpio.net/s/1lo1
You can use array_reduce to reimplement array_unique, but collecting unique types in order of appearance. This will help us to sort array later.
$order = [
'PHOTOS' => 1,
'VIDEOS' => 2,
'DOCUMENTS' => 3
];
$types = [];
$uniqueDownloads = array_reduce(
$downloads,
function ($uniqueDownloads, $download) use (&$types, $order) {
$type = $download['TYPE'];
if (!isset($types[$type])) {
$types[$type] = isset($order[$type]) ? $order[$type] : 0;
$uniqueDownloads[] = $download;
}
return $uniqueDownloads;
},
[]
);
array_multisort($types, $uniqueDownloads);
Traversing the array we check whether the type key exists in $types. If it exists skip this element. Otherwise, set this key and assign value from $order to it (this will be used for sorting).
We then use array_multisort to sort $uniqueDownloads by sorting $types.
Here is working demo.
Read more about anonymous functions. I have passed $types by reference with & so the function change value of the original array, but not the value of the copy.
For custom ordering I recommended usort:
$array = [
['TYPE'=>'PHOTOS'],
['TYPE'=>'DOCUMENTS'],
['TYPE'=>'VIDEOS']
];
function customOrder($a, $b){
$newOrder = [
'PHOTOS' => 1,
'VIDEOS' => 2,
'DOCUMENTS' => 3
];
$valA = (array_key_exists($a['TYPE'],$newOrder))?$newOrder[$a['TYPE']]:999;
$valB = (array_key_exists($b['TYPE'],$newOrder))?$newOrder[$b['TYPE']]:999;
if($valA > $valB){
$result = 1;
} elseif($valA < $valB){
$result = -1;
} else {
$result = 0;
}
return $result;
}
usort($array, "customOrder");
var_dump($array);
Because your ordering array appears to be an exhaustive list of possible download values, you can just filter that static array by the downloads values.
Code: (Demo)
var_export(
array_intersect(
$new_order,
array_column($downloads, 'type')
)
);
If there may be values in the downloads array that are not represented in the ordering array, then the sample data in the question should be adjusted to better represent the application data.

Sum values in array and simplify key names

This is almost similar to my other question which is related to the same project I'm working on.. Link to my other question
but in this case the array is different as follow:
Array
(
[2014-08-01 11:27:03] => 2
[2014-08-01 11:52:57] => 2
[2014-08-01 11:54:49] => 2
[2014-08-02 11:59:54] => 4
[2014-08-02 12:02:41] => 2
[2014-08-05 12:09:38] => 4
[2014-08-07 12:23:12] => 3
[2014-08-07 12:25:18] => 3
// and so on...
)
That is my output array and in order to get that array I had to do some miracles... anyway, so based on that array I have to sum the value for each key date and build an array something like this...
Array
(
[2014-08-01] => 6
[2014-08-02] => 6
[2014-08-05] => 4
[2014-08-07] => 6
// and so on...
)
That last array will be use to build graphs with morrisonJS, what I have is this:
$res_meno = array();
foreach ($sunArr as $keys => $values) {
$arrays= explode(" ",$sumArr[$keys]);
$res_meno[] = $arrays[0];
}
$vals_char2 = array_count_values($res_meno);
That is my attempt to build my last array but is not working...
any help would be greatly appreciated!
Thank you for taking the time.
Try this code
<?php
$arr = array(
"2014-08-01 11:27:03" => 2,
"2014-08-01 11:52:57" => 2,
"2014-08-01 11:54:49" => 2,
"2014-08-02 11:59:54" => 4,
"2014-08-02 12:02:41" => 2,
"2014-08-05 12:09:38" => 4,
"2014-08-07 12:23:12" => 3,
"2014-08-07 12:25:18" => 3
);
$new_array = array();
foreach($arr as $k => $v){
$date = reset(explode(" ", $k));
if(isset($new_array[$date])){
$new_array[$date] += $v;
}
else{
$new_array[$date] = $v;
}
}
print_r($new_array);
?>
DEMO
$sunArr = array
(
"2014-08-01 11:27:03" => 2,
"2014-08-01 11:52:57" => 2,
"2014-08-01 11:54:49" => 2,
"2014-08-02 11:59:54" => 4,
"2014-08-02 12:02:41" => 2,
"2014-08-05 12:09:38" => 4,
"2014-08-07 12:23:12" => 3,
"2014-08-07 12:25:18" => 3,
);
$res_meno = array();
foreach ($sunArr as $keys => $values) {
$arrays= explode(" ",$keys);
if(isset($res_meno[$arrays[0]]))
{
$res_meno[$arrays[0]] = $res_meno[$arrays[0]] + $values;
}
else
{
$res_meno[$arrays[0]] = $values;
}
}
print_r($res_meno);
exit;
Try this, i think it might fix the problem
Try this PHP Code You Can test here
$sunArr = Array
(
'2014-08-01 11:27:03' => 2,
'2014-08-01 11:52:57' => 2,
'2014-08-01 11:54:49' => 2,
'2014-08-02 11:59:54' => 4,
'2014-08-02 12:02:41' => 2,
'2014-08-05 12:09:3' => 4,
'2014-08-07 12:23:12' => 3,
'2014-08-07 12:25:18' => 3
);
$key = 0;
$res_meno = array();
foreach ($sunArr as $keys => $values)
{
$ar= explode(" ", $keys);
if( $key == $ar[0] )
{
$res_meno[$key] = $sunArr[$keys] + $res_meno[$key];
}
else
{
$key = $ar[0];
$res_meno[$key] = $values;
}
}
echo '<pre>';
print_r($res_meno);
die;
Where does the first array come from? If it's from a SQL database, it's better to create a query that returns the aggregated array.
Otherwise no need to use array_key_values for that:
$res_meno = array();
foreach ($sumArr as $keys => $values) {
$key = substr($keys, 0, 10);
$res_meno[$key] = (empty($res_meno[$key]) ? 0 : $res_meno[$key]) + $values;
}
Here is a solution which uses a callback. Not to use loops is often better!
$sunArr = array(
'2014-08-01 11:27:03' => 3,
'2014-08-01 11:27:05' => 5,
'2013-09-01 11:01:05' => 1
);
$res = array();
function map($item, $key, &$result)
{
$result[current(explode(" ", $key))] += $item;
}
array_walk($sunArr, "map", &$res);
var_dump($res);
You can test it here on codepad.

Create assoc array from array of asoc array

How can I create new array with structure: [id] => [prop] from data like this
$foo = array(
array('id' => 2, 'prop' => 'val2'),
array('id' => 1, 'prop' => 'val1'),
array('id' => 3, 'prop' => 'val3'),
);
but in elegant way, without foreach loop?
In PHP 5.5:
$result = array_column($foo, 'prop', 'id');
For PHP<5.5:
$result = array_combine(
array_map(function($x)
{
return $x['id'];
}, $foo),
array_map(function($x)
{
return $x['prop'];
}, $foo),
);
I have to note, that despite all is hidden inside callbacks or even built-in functions, it's still loop inside - thus, in terms of complexity it's same as plain loop.
For PHP < 5.5, you could also use array_reduce to get the result:
$result = array_reduce($foo, function($v, $w) {
$v[$w['id']] = $w['prop'];
return $v;
});

Joining rows from two 2d arrays where a common column value is found

I have two arrays that I would like to join into one. Both arrays have a common key=>value and I would like to insert the values of one array to the other so that I to create one array.
$array1 = [
['ID' => 123456, 'Key' => 1000, 'value' => 123.45],
['ID' => 789012, 'Key' => 1001, 'value' => 56748.17],
];
$array2 = [
['Key' => 1000, 'description' => 'desc1'],
['Key' => 1001, 'description' => 'desc2'],
];
I would like to join Array2 with Array1 so that the resulting Array is as follows:
array (
0 =>
array (
'ID' => 123456,
'Key' => 1000,
'value' => 123.45,
'description' => 'desc1',
),
1 =>
array (
'ID' => 789012,
'Key' => 1001,
'value' => 56748.17,
'description' => 'desc2',
),
)
So the arrays have been joined using the [Key] value as the, well, key. I've looked at array_merge and other function but I can't seem to get these two arrays to "merge" properly.
try this, its linear
$keyval = array();
foreach($array1 as $item)$keyval[$item['Key']] = $item['value'];
foreach($array2 as $key=>$item)$array2[$key]['description'] = isset($keyval[$item['Key']]) ? $keyval[$item['Key']] : '';
You would have to do something like
$result = array();
foreach ($a1 as $v1)
{
foreach ($a2 as $k2 => $v2)
{
if ($v1['Key'] === $v2['Key'])
{
$result[] = array_merge($v1, $v2);
unset($a2[$k2]);
break;
}
}
}
Version with for loops
$result = array();
$c_a1 = count($a1);
$c_a2 = count($a2);
for ($i = 0; $i < $c_a1; $i++)
{
for ($j = 0; $j < $c_a2; $j++)
{
if ($a1[$i]['Key'] === $a2[$j]['Key'])
{
$result[] = array_merge($a1[$i], $a2[$j]);
unset($a2[$j]);
$c_a2--;
break;
}
}
}
This is my approach:
$temp_ array = array_fill_keys (array_map(create_function('$a', 'return $a["Key"];'), $array_1) , $array_1);
$result = array();
foreach ($array_2 as $item) {
if (isset($temp_array[$item['Key']])) {
$result[] = array_merge($item, $temp_array[$item['Key']]);
}
}
I have elaborated more in the code above, and reached this improved version:
function array_merge_items_by_common_key_value($key, $array_1, $array_2)
{
$result = array();
$temp_ array = array_fill_keys(array_map(create_function('$a', 'return $a["' . $key . '"];'), $array_1) , $array_1);
foreach ($array_2 as $item)
{
$result[$item[$key]] = isset($temp_array[$item[$key]]) ? array_merge($item, $temp_array[$item[$key]]) : $item;
}
return array_values(array_merge($result, array_diff_key($array_1, $result)));
}
$merged_arrays = array_merge_items_by_common_key_value('Key', $temp_array, $array_2);
First, a temporary array is created: it is equal to $array_1, but its keys are the values to be matched.
Then, $array_2 is looped. When a match is found, the merge is done. If there is no match, then the $array_2 value is maintained, untouched.
Finally, those values in the $array_1 which were not matched, are also appended to the resulting array.
So, no item of both $array_1 or $array_2 is lost, while the matched items are merged.
#radashk's solution will work if you can always guarantee that $array1[$i] corresponds to $array2[$i]. From my reading of the question, that's not guaranteed, but instead you want to make sure that $array1[$i]['Key'] == $array2[$j]['Key'], and combine elements where those Keys match.
There may be a more elegant solution, but I would do it like this:
// builds up new $tmpArray, using the Key as the index
$tmpArray = array();
foreach($array1 as $innerArray1){
$tmpArray[$innerArray1['Key']] = $innerArray1;
}
//Merges the values from $array2 into $tmpArray
foreach($array2 as $innerArray2) {
if (isset($tmpArray[$innerArray2['Key']])) {
$tmpArray[$innerArray2['Key']] = array_merge($tmpArray[$innerArray2['Key']], $innerArray2);
}else{
$tmpArray[$innerArray2['Key']] = $innerArray2;
}
}
Use temporary first level keys to swiftly identify matching Key values between the two arrays. When an array2 row qualifies for merger with the first, use the union-assignment operator (+=). Call array_value() after looping if you don't want to preserve the temporary keys.
Code: (Demo)
$result = array_column($array1, null, 'Key');
foreach ($array2 as $row) {
if (isset($result[$row['Key']])) {
$result[$row['Key']] += $row;
}
}
var_export(array_values($result));

Categories