We are running a PHP code on a huge list (array with 5 millions elements).
The list format is as follows below (it looks "weird" at first look but this is the best format so far we came out in order to optimize the speed of the code down below)
$array = array(
array(1 => true,3 => true),
array(2 => true,4 => true,6 => true),
array(3 => true,5 => true),
array(5 => true),
array(4 => true,8 => true,10 => true),
array(200 => true,300 => true)
);
We want to combine similar elements from the array above and get to this result:
$final_array = array(
array(1,3,5),
array(2,4,6,8,10),
array(200,300)
);
Instead of using array(1,3) we decided to use array(1 => true,3 => true) because using keys instead of values (to store information) makes the code below runs faster and it outputs $final_array as above.
foreach ($array as $key1 => $value1) {
foreach ($array as $key2 => $value2) {
if ($key1 != $key2) {
foreach ($array[$key1] as $key3 => $value3) {
if (isset($array[$key2][$key3])) {
$array[$key2] = $array[$key2] + $array[$key1];
unset($array[$key1]);
break 2;
}
}
}
}
}
However this code above is still very slow. Could you find a better way to aggregate similar elements to each other on a faster code?
How about using array_intersect_key instead of third loop?
$array = array(
array(1 => true,3 => true),
array(2 => true,4 => true,6 => true),
array(3 => true,5 => true),
array(5 => true),
array(4 => true,8 => true,10 => true),
array(200 => true,300 => true)
);
foreach ($array as $key => $value) {
foreach ($array as $key2 => $value2) {
if ($key !== $key2 && !empty(array_intersect_key($value, $value2))) {
$array[$key] = $value2 + $value;
unset($array[$key2]);
}
}
}
print_r($array);
Working example
Edit #1:
For better performance try this variant:
$array = array(
array(1 => true,3 => true),
array(2 => true,4 => true,6 => true),
array(3 => true,5 => true),
array(5 => true),
array(4 => true,8 => true,10 => true),
array(200 => true,300 => true)
);
$count = count($array);
for ($i = 0; $i < $count - 1; ++$i) {
for ($j = $i + 1; $j < $count; ++$j) {
if (!empty(array_intersect_key($array[$i], $array[$j]))) {
$array[$j] = $array[$i] + $array[$j];
unset($array[$i]);
continue 2;
}
}
}
Working example #2
$arr = array('Hello','World!','Beautiful','Day!');
echo join(" ",$arr);
See Details
Related
So I recently got into this school project and now I have to deal with multidimensional arrays in PHP. My problem:
I have an array:
$arr = array(
array('subject' => 'Deutsch'),
array('subject' => 'Deutsch'),
array('subject' => 'Deutsch')
);
And now I want to make it look like:
array(
array('id' => 1, 'subject' => 'Deutsch'),
array('id' => 2, 'subject' => 'Deutsch'),
array('id' => 3, 'subject' => 'Deutsch')
);
I thought of doing something like:
foreach ($arr as $key => $value) {
array_unshift($arr, array('id' => $key + 1));
}
But this doesn't give me the expected output. How can I modify my code to get to my goal?
You can array_merge() the id to each subArray, e.g.
foreach ($arr as $key => $value) {
$arr[$key] = array_merge(["id" => $key + 1], $value);
}
You can itterate the array by reference and add the id element:
foreach($arr as $key => &$val)
{
$val['id'] = $key +1;
}
I've spent a while trying to get what I need from old answers but haven't quite got it (have got close though!).
I have this;
[January] => Array
(
[Tuesday] => Array
(
[foo] => Array
(
[82] => 47731
[125] => 19894
)
[bar] => Array
(
[82] => 29911
[125] => 10686
)
)
}
...and I want this;
[0] => Array
(
'key' => 'January'
'children' => Array
[0] => Array
{
'key' => 'Tuesday'
'children' => Array
[0] => Array
{
'key' => 'foo'
'values' => Array
{
[82] => 47731
[125] => 19894
}
[1] => Array
{
'key' => 'bar'
'values' => Array
{
[82] => 29911
[125] => 10686
}
}
)
}
I've got fairly close by adapting the first answer from Recursively change keys in array but only the bottom layer of my result is correct - the nodes with keys 'Tuesday', 'foo' and 'bar' just look the same as in the source array.
Here's what I've got so far;
public function transform_hierarchical_output(&$var)
{
if (is_array($var))
{
$final = [];
$i = 0;
foreach ($var as $k => &$v)
{
$new_node = [
'key' => $k,
'children' => $v
];
$k = $i;
$this->transform_hierarchical_output($v);
$final[$k] = $new_node;
$i++;
}
$var = $final;
}
elseif (is_string($var))
{
}
}
This needs to work with a source array of any length and depth.
Thanks in advance.
Geoff
<?php
$array = [
'January' => [
'Tuesday' => [
'foo' => [
82 => 47731,
125 => 19894,
],
'bar' => [
82 => 47731,
125 => 19894,
]
]
]
];
function transform(array $input)
{
$output = [];
foreach ($input as $key => $val) {
if (is_array(array_values($val)[0])) { // if next depth is an array
$output[] = [
'key' => $key,
'children' => transform($val)
];
} else {
$output[] = [
'key' => $key,
'values' => $val
];
}
}
return $output;
}
print_r(transform($array));
try below:
function t($arr)
{
$a = [];
$num = 0;
foreach($arr as $k => $v) {
if (is_array($v))
{
$a[$num] = [
'key' => $k,
];
$a[$num][is_array(array_values($v)[0]) ? 'children' : 'values'] = t($v);
$num ++;
} else {
$a[$k] = $v;
}
}
return $a;
}
First thing I should note is that, though there are many recursions in your example, but technically it is not a recursive loop in terms of the need for a self-executing function. Because your loop's scheme fluctuates a bit in its depth, it does not follow a regular pattern down to is last node; however it is a logical scheme, but is not repeated down to is last node.
The following function might work for you:
function doArray($array)
{
$keys = array_keys($array);
$arr_count = count($keys);
$new_array;
for($i = 0; $i < $arr_count; $i++)
{
$new_array[$i]["key"] = $keys[$i];
$new_keys = array_keys($array[$keys[$i]]);
for($w = 0; $w < count($new_keys); $w++)
{
$new_array[$i]["children"][$w]["keys"] = $new_keys[$i];
$new_array[$i]["children"][$w]["children"] = array();
for($w = 0; $w < count($new_keys); $w++)
{
$new_new_keys = array_keys($array[$keys[$i]][$new_keys[$w]]);
for($q = 0; $q < count($new_new_keys); $q++)
{
$new_array[$i]["children"][$w]["children"][$q]["key"] = $new_new_keys[$q];
//$new_array[$i]["children"][$w]["children"][$q]["children"] = $array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]];
$last_new_keys = array_keys($array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]]);
for($s = 0; $s < count($last_new_keys); $s++)
{
$new_array[$i]["children"][$w]["children"][$q]["values"][$last_new_keys[$s]] = $array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]][$last_new_keys[$s]];
}
}
}
}
}
return $new_array;
}
I got two associative, multidimensional arrays $arrayOffered and $arraySold. I would like to merge them under certain conditions:
if value of key 'item' from $arrayOffered exists in $arraySold, both elements should be included in array $result. If for 1 element from $arrayOffered there are 3 elements in $arraySold, I should get also 3 elements in $result.
otherwise, element from $arrayOffered should be added into $result.
One element from $arrayOffered can have >1 equivalents in $arraySold. They should be joined in the way shown below.
Input data:
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
Desired result:
$desiredResult = array(
0 => array('item' => 'product_1', 'Category' => 'ABC', 'ItemsSold' => '2', 'ItemsReturned' => 1),
1 => array('item' => 'product_1', 'Category' => 'ABC', 'ItemsSold' => '1'),
2 => array('item' => 'product_2', 'Category' => 'DEF')
);
I got stuck on something like:
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$i = 0;
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i][] = $test;
$i++;
}
else
{
$result[$i][] = $offeredSubArr;
$i++;
}
}
}
Problem:
- output array isn't formatted the way I wanted
- I know I'm not going in the right direction. Can you please give me a hint?
This is an option, since you have this $arrayOffered as a kind of master file I suggest to build a hash with this array and use later on the foreach look for sold array.
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
//Build a hash to get the extra properties
$hashArray = array();
foreach ($arrayOffered as $offered) {
$hashArray[$offered['item']]=$offered;
}
$resultArray = array();
foreach ($arraySold as $sold) {
$hashItem = $hashArray[$sold['item']];
// you dont want this sold flag on your final result
unset($hashItem['sold']);
$resultArray[]=array_merge($hashItem,$sold);
$hashArray[$sold['item']]['sold']= true;
}
//Add all the missing hash items
foreach($hashArray as $hashItem){
if(!isset($hashItem['sold'])){
$resultArray[]=$hashItem;
}
}
print_r($resultArray);
Test sample
http://sandbox.onlinephpfunctions.com/code/f48ceb3deb328088209fbaef4f01d8d4430478db
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{ $i = 0;
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i][] = $test;
}
else
{
$result[$i][] = $offeredSubArr;
}
$i++;
}
}
$result = $result[0];
echo '<pre>'; print_r($result); die();
Well i will try to follow your logic although there is simpler solutions.
First of all we will need to search in a multidimentional array thats why we will need the followed function from this so thread
function in_array_r($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
Next after small changes:
$i you don't need to make it zero on every loop just once so place it outside
unnecessary [] ($result[$i][]) you don't need the empty brackets no reason to create an extra table in the $i row since what you add there, the $test is already table itself
Adding the last loop coz when sth is not in the second table it will be added in your new table in every loop and as far as i get you don't want that kind of duplicates
We have the following code:
$arrayOffered = array(
0 => array('item' => 'product_1', 'Category' => 'ABC'),
1 => array('item' => 'product_2', 'Category' => 'DEF')
);
$arraySold = array(
0 => array('item' => 'product_1', 'ItemsSold' => '2', 'ItemsReturned' => 1), //arrays in this array can contain up to 30 elements
1 => array('item' => 'product_1', 'ItemsSold' => '1')
);
$i = 0;
$result = array();
foreach ($arrayOffered as $keyOffered => $offeredSubArr)
{
$item = $offeredSubArr['item'];
foreach($arraySold as $keySold => $soldSubArr)
{
if(isset($soldSubArr['item']) && $soldSubArr['item'] == $item)
{
$test = array_merge($offeredSubArr, $soldSubArr);
$result[$i] = $test;
$i++;
}
}
}
foreach ($arrayOffered as $value)
{
if (!in_array_r($value['item'], $result))
{
$result[$i] = $value;
$i++;
}
}
print_r($result);
Which as far as i tested gives the wanted result.
I want to convert this.
$data1 = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
into this
$data2 = array(
'value' => array(0 => '100.00', 1 => '10.00', 2 => '200.00', 3 => '190.00'),
'total' => array(0 => '32', 1 => '13', 2 => '39', 3 => '11')
);
I can obviously do this in a roundabout way by iterating over the top array, while appending to a series of arrays, but I figured that there must be a php array function that I don't know about that can do this more concisely.
http://www.php.net/manual/en/ref.array.php
Values are floats and integers (if it makes any difference), I've just added them as strings in the example code because it's easier to read IMO. Final array order should match the initial order. I'll award the correct answer to the least LOC providing performance isn't significantly worse that the 'long' version. PHP 5.4.
If PHP had an array_pluck function, it would be simple.
function array_pluck(array $array, $field)
{
return array_map(function($row) use ($field) { return $row[$field]; }, $array);
}
$data2 = array(
'value' => array_pluck($data1, 'value'),
'total' => array_pluck($data1, 'total')
);
I think that's about as easy as it is to read, but you'll be looping over the entire array once per field, so it's hardly the optimal solution.
Personally, this is a situation where I'd probably stick with the foreach solution but try to wrap it inside some reusable function.
<?php // php 5.4 array syntax
$new = array_reduce($data1, function (&$result, $item)
{
$result['value'][] = $item['value'];
$result['total'][] = $item['total'];
return $result;
},
['value' => [], 'total' => []]);
'value' and 'total' are arbitrary names, so you're not going to get a one-liner php library function to do this.
You can refactor this code into a function if you want to...
function array_rotate($data) {
$k = array_keys($data[0]);
return array_reduce($data, function (&$r, $i) use ($k) {
$r[$k[0]][] = $i[$k[0]];
$r[$k[1]][] = $i[$k[1]];
return $r;
}, [$k[0] => [], $k[1] => []]);
}
I generalised #matthew's code, this allows an arbitrary number of keys (instead of 2):
function array_rotate2($data) {
return array_combine(array_keys($data[0]),
array_map(function ($field) use ($data) {
return array_map(function($row) use ($field) { return $row[$field]; }, $data);
}, array_keys($data[0])));
}
You can do it without a function using a simple foreach():
<?php
$data = array(
array('value' => '100.00', 'total' => '32'),
array('value' => '10.00', 'total' => '13'),
array('value' => '200.00', 'total' => '39'),
array('value' => '190.00', 'total' => '11'),
);
$newArray = array();
$i=0;
foreach($data as $value){
$newArray["value"][] = $data[$i]["value"];
$newArray["total"][] = $data[$i]["total"];
$i++;
}
echo "<pre>";
print_r($newArray);
echo "</pre>";
?>
Prints this:
Array
(
[value] => Array
(
[0] => 100.00
[1] => 10.00
[2] => 200.00
[3] => 190.00
)
[total] => Array
(
[0] => 32
[1] => 13
[2] => 39
[3] => 11
)
)
$final = array();
foreach($data1 as $array) {
foreach($array as $key => $value) {
$final[$key] = isset($final[$key]) ? $final : array();
$final[$key][] = $value;
}
}
Like others are saying I don't think there is a one-liner. Here is a reusable foreach function that should work
function array_multi_key_combine($a, $keys = array()) {
$b = array();
foreach($a as $v) {
foreach($keys as $k) {
if(isset($v[$k])) $b[$k][] = $v[$k];
}
}
return $b;
}
$data2 = array_multi_key_combine($data1, array('value', 'total'));
public function getCheckoutForm(){
$arr = array(
'cmd' => '_cart',
'business' => 'some#mail',
'no_shipping' => '1',
'upload' => '1',
'return' => 'url',
'cancel_return' => 'url1',
'no_note' => '1',
'currency_code' => 'url2',
'bn' => 'PP-BuyNowBF');
$cpt=1;
foreach($this->items as $item){
$arr1[] = array(
'item_number_'.$cpt.'' => $item['item_id'],
'item_name_'.$cpt.'' => $item['item_name'],
'quantity_'.$cpt.'' => $item['item_q'],
'amount_'.$cpt.'' => $item['item_price']
);
$cpt++;
}
return array_merge($arr,$arr1[0],$arr1[1]);
}
This returns array like that:
Array
(
[cmd] => _cart
[business] => some#mail
[no_shipping] => 1
[upload] => 1
[return] => url1
[cancel_return] =>url2
[no_note] => 1
[currency_code] => EUR
[bn] => PP-BuyNowBF
[item_number_1] => 28
[item_name_1] => item_name_1
[quantity_1] => 1
[amount_1] => 5
[item_number_2] => 27
[item_name_2] => item_name_2
[quantity_2] => 1
[amount_2] => 30
)
The problem is that in return $arr1[0] and $arr1[1] are hardcoded. And if in loop i have more than 2 arrays, lets say 0,1,2,3 ans so on, this code won't work. Any idea? Maybe my logic is compleatly wrong...
There's no need to create arrays in your loop - just add new keys directly to the first array:
public function getCheckoutForm(){
$arr = array(
'cmd' => '_cart',
'business' => 'some#mail',
'no_shipping' => '1',
'upload' => '1',
'return' => 'url',
'cancel_return' => 'url1',
'no_note' => '1',
'currency_code' => 'url2',
'bn' => 'PP-BuyNowBF'
);
$cpt=1;
foreach($this->items as $item){
$arr['item_number_'.$cpt] = $item['item_id'];
$arr['item_name_'.$cpt] = $item['item_name'];
$arr['quantity_'.$cpt] = $item['item_q'];
$arr['amount_'.$cpt] = $item['item_price'];
$cpt++;
}
return $arr;
}
I would probably do something like
$count = count($arr1);
for($i=0;$i<$count;$i++){
$arr = array_merge($arr,$arr1[$i]);
}
return $arr;
I hope, I understood, what you mean ^^
foreach ($i = 0, $n = count($arr1); $i < $n; $i++) {
$arr = array_merge($arr, $arr1[$i]);
}
return $arr;
You could do the merge in every iteration:
foreach($this->items as $item){
$temp_arr = array(
'item_number_'.$cpt.'' => $item['item_id'],
'item_name_'.$cpt.'' => $item['item_name'],
'quantity_'.$cpt.'' => $item['item_q'],
'amount_'.$cpt.'' => $item['item_price']
);
$arr = array_merge($arr,$temp_arr)
$cpt++;
}
which has the advantage that you could possibly get $temp_arr from a function,
or just add all the elements to one array:
foreach($this->items as $item){
$arr['item_number_'.$cpt.''] => $item['item_id'];
$arr['item_name_'.$cpt.''] => $item['item_name'];
$arr['quantity_'.$cpt.''] => $item['item_q'];
$arr['amount_'.$cpt.''] => $item['item_price'];
$cpt++;
}
do this
$count = count($data);
$sum = 1;
$arr = [];
for($i=0;$i<$count;$i++){
$temp = $arr;
if($i == $count - 1){
$sum = 0;
}
$arr = array_merge($temp,$data[$i + $sum]);
}
return $arr;