How to get from array:
$a = [
'color' => ['red', 'blue', ....∞],
'size' => ['10 -12 ', '12 -14', ....∞],
.....∞
];
such an array
$b = [
['color' => 'red', 'size' => '10 -12 '],
['color' => 'blue', 'size' => '10 -12 '],
['color' => 'red', 'size' => '12 -14 '],
['color' => 'blue', 'size' => '12 -14 '],
];
keys and values can be any number,
the names of keys and values can be different
php cross join
I tried to do it, but it's limited
foreach($a['color'] as $k1 => $v1){
foreach($a['size'] as $k2 => $v2){
$d[] = ['color' => $v1, 'size' => $v2];
}
}
no one here can solve it, apparently it's difficult, I found a solution
Solution 1
function cross($array)
{
$result = [];
$current = array_splice($array, -1);
foreach ($current as $key => $values) {
foreach ( $values as $value) {
if (empty($array)) {
array_push($result, [$key => $value]);
} else {
foreach (cross($array) as $temp) {
array_push($result, array_merge([$key => $value], $temp));
}
}
}
}
return $result;
}
print_r(cross($a));
Solution 2
function cartesian($input) {
$result = array();
while (list($key, $values) = each($input)) {
// If a sub-array is empty, it doesn't affect the cartesian product
if (empty($values)) {
continue;
}
// Seeding the product array with the values from the first sub-array
if (empty($result)) {
foreach($values as $value) {
$result[] = array($key => $value);
}
}
else {
// Second and subsequent input sub-arrays work like this:
// 1. In each existing array inside $product, add an item with
// key == $key and value == first item in input sub-array
// 2. Then, for each remaining item in current input sub-array,
// add a copy of each existing array inside $product with
// key == $key and value == first item of input sub-array
// Store all items to be added to $product here; adding them
// inside the foreach will result in an infinite loop
$append = array();
foreach($result as &$product) {
// Do step 1 above. array_shift is not the most efficient, but
// it allows us to iterate over the rest of the items with a
// simple foreach, making the code short and easy to read.
$product[$key] = array_shift($values);
// $product is by reference (that's why the key we added above
// will appear in the end result), so make a copy of it here
$copy = $product;
// Do step 2 above.
foreach($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}
// Undo the side effecst of array_shift
array_unshift($values, $product[$key]);
}
// Out of the foreach, we can add to $results now
$result = array_merge($result, $append);
}
}
return $result;
}
Is this what you are trying to achieve? Note that for it to work correctly, the number of parameters for color and size must be the same!
<?php
$b = ['color' => ['red', 'blue', 'yellow'],'size' => ['10 -12 ', '12 -14', '12-16']];
for ($counter=0; $counter < sizeof($b['color']) ; $counter++) {
$d[$counter]['color'] = $b['color'][$counter];
$d[$counter]['size'] = $b['size'][$counter];
}
echo "<pre>";
print_r($d);
?>
Related
How can I add all the columnar values by associative key? Note that key sets are dynamic and some key value is more than one.
Example array :
$myArray = array(
["apple" => 2,"orange" => 1,"lemon" => 4,"grape" => 5],
["apple" => 5,"orange" => 0,"lemon" => 3,"grape" => 2],
["apple" => 3,"orange" => 0,"lemon" => 1,"grape" => 3],
);
With single key value I can sum the column value easily with the code below.
$sumArray = array();
foreach ($myArray as $k => $subArray)
{
foreach ($subArray as $id => $value)
{
if (array_key_exists($id, $sumArray))
{
$sumArray[$id] += $value;
} else {
$sumArray[$id] = $value;
}
}
}
echo json_encode($sumArray);
the result will be like these:
{"apple":10,"orange":1,"lemon":8,"grape":10}
For multiple key values the code above is not working. How to sum column values if key value is more than one?
Example array:
$myArraymulti = array(
["apple" => 2,"orange" => 1,"lemon" => [4, 2],"grape" => 5],
["apple" => 5,"orange" => [0, 2],"lemon" => 3,"grape" => 2],
["apple" => 3,"orange" => 0,"lemon" => 1,"grape" => [3, 8]],
);
Desired result:
{"apple":10,"orange":3,"lemon":10,"grape":18}
Check if the value is an array. If it is, use the sum of the elements instead of the value itself when adding to the associative array.
foreach ($myArray as $k => $subArray)
{
foreach ($subArray as $id => $value)
{
if (is_array($value)) {
$value_to_add = array_sum($value);
} else {
$value_to_add = $value;
}
if (array_key_exists($id, $sumArray))
{
$sumArray[$id] += $value_to_add;
} else {
$sumArray[$id] = $value_to_add;
}
}
}
I suggest you fix your data structure, though. You should just make every value an array, rather than mixing singletons and arrays, so you don't have to use conditionals in all the code that processes the data.
$keys = [];
forEach($myArraymulti as $array){
$keys = array_merge(array_keys($array), $keys);
}
$res = [];
forEach(array_unique($keys) as $key){
forEach($myArraymulti as $array){
if(isset($array[$key])){
$res[$key] = (isset($res[$key]) ? $res[$key] : 0) +
(is_array($array[$key]) ? array_sum($array[$key]) : $array[$key]);
}
}
}
$res = json_encode($res));
This question already has answers here:
Group 2d array rows by one column and sum another column [duplicate]
(3 answers)
Closed 4 months ago.
I have a multi array that has some duplicated values that are same by name ( name is an element )
i want to sum quantity of each array that has same name , and then unset the second array
Example :
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
?>
Now i want to sum duplicated values as below.
Result :
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>750),
1=>array("name"=>"y","QTY"=>250)
);
?>
UPDATED
i found this function to search in array , foreach and another loops does not works too
<?php
function search($array, $key, $value)
{
$results = array();
if (is_array($array)) {
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
$results = array_merge($results, search($subarray, $key, $value));
}
}
return $results;
}
?>
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
$result = array();
$names = array_column($Array, 'name');
$QTYs = array_column($Array, 'QTY');
$unique_names = array_unique($names);
foreach ($unique_names as $name){
$this_keys = array_keys($names, $name);
$qty = array_sum(array_intersect_key($QTYs, array_combine($this_keys, $this_keys)));
$result[] = array("name"=>$name,"QTY"=>$qty);
}
var_export($result); :
array (
0 =>
array (
'name' => 'X',
'QTY' => 750,
),
1 =>
array (
'name' => 'y',
'QTY' => 250,
),
)
Try this simplest one, Hope this will be helpful.
Try this code snippet here
$result=array();
foreach ($Array as $value)
{
if(isset($result[$value["name"]]))
{
$result[$value["name"]]["QTY"]+=$value["QTY"];
}
else
{
$result[$value["name"]]=$value;
}
}
print_r(array_values($result));
Try this, check the live demo.
<?php
$Array=array(
0=>array("name"=>"X","QTY"=>500),
1=>array("name"=>"y","QTY"=>250),
2=>array("name"=>"X","QTY"=>250)
);
$keys = array_column($Array, 'name');
$QTYs = array_column($Array, 'QTY');
$result = [];
foreach($keys as $k => $v)
{
$result[$v] += $QTYs[$k];
}
print_r($result);
You can achieve this by creating an array with name as key and then iterating over all values and add them together, resulting in this
function sum_same($array) {
$keyArray = [];
foreach ($array as $entry) {
$name = $entry["name"];
if(isset($keyArray[$name])) {
$keyArray[$name] += $entry["QTY"];
} else {
$keyArray[$name] = $entry["QTY"];
}
}
// Convert the keyArray to the old format.
$resultArray = [];
foreach ($keyArray as $key => $value) {
$resultArray[] = ["name" => $key, "QTY" => $value];
}
return $resultArray;
}
Try the code here
If you want to alter the old array use the function like this:
$myArray = sum_same($myArray);
The old array will be overwritten by the new one.
This problem is a classic example of usage for array_reduce():
$Array = array(
0 => array('name' => 'X', 'QTY' => 500),
1 => array('name' => 'y', 'QTY' => 250),
2 => array('name' => 'X', 'QTY' => 250),
);
// array_values() gets rid of the keys of the array produced by array_reduce()
// they were needed by the callback to easily identify the items in the array during processing
$Array = array_values(array_reduce(
$Array,
function (array $a, array $v) {
$k = $v['name'];
// Check if another entry having the same name was already processed
// Keep them in the accumulator indexed by name
if (! array_key_exists($k, $a)) {
$a[$k] = $v; // This is the first entry with this name
} else {
// Not the first one; update the quantity
$a[$k]['QTY'] += $v['QTY'];
}
return $a; // return the partial accumulator
},
array() // start with an empty array as accumulator
));
My array looks like the following:
Array
(
[0] => Array
(
[index] => 0
[quantity] => 1
[0] => Array
(
[id_product] => 20
[title] => Oranges
)
)
[1] => Array
(
[index] => 1
[quantity] => 1
[0] => Array
(
[id_product] => 24
[title] => Bananas
)
)
)
To make this array, this is my code:
$i = 0;
$content = array();
if(isset($_SESSION['cart'])){
foreach($_SESSION['cart'] as $result){
foreach($result as $item){
$values = $product->getById($item['id']);
if($values != null){ // which means it has product
/*
Checks if the array already contains that ID
this avoids duplicated products
*/
if(main::search_multidimensional($content, "id_product", $item['id_product']) == null){
$content[] = array("index" => $i, "quantity" => 1, $values);
$i++;
}else{ /*
in case it does have already the id_product in the array
I should update the "quantity" according to the "index".
*/
}
}
}
}
}
return $content;
My problem is after the }else{. I've been trying some codes without any success. I have to update the quantity according to the index. Although if you guys think there's a better alternative please let me know.
Edit: Since most of the people is worried about the search_multidimensional and it might be my solution, here's the function:
public function search_multidimensional($array, $key, $value){
$results = array();
if (is_array($array)) {
if (isset($array[$key]) && $array[$key] == $value) {
$results[] = $array;
}
foreach ($array as $subarray) {
$results = array_merge($results, self::search_multidimensional($subarray, $key, $value));
}
}
return $results;
}
EDIT 2:
In that case, would this help? (Since your search_multidimensional only returns a true or false)
$i = 0;
$content = array();
if(isset($_SESSION['cart'])){
foreach($_SESSION['cart'] as $result){
foreach($result as $item){
$values = $product->getById($item['id']);
if($values != null) { // which means it has product
/*
Checks if the array already contains that ID
this avoids duplicated products
*/
$product_exists = false;
foreach($content as &$cItem) {
if($cItem['values']['id_product'] == $item['id_product']) {
$cItem['values']['quantity']++; // Increments the quantity by 1
$product_exists = true;
break;
}
}
// If the product does not exist in $content, add it in.
if(!$product_exists)
$content[] = array("index" => $i, "quantity" => 1, "values" => $values);
$i++;
}
}
}
}
(Edited again to give an array key to $values)
OLD ANSWER:
Since you are recreating the cart array in $content, you could just do this in your else:
$content[] = array("index" => $i, "quantity" => $result['quantity'] + 1, $values);
Such that it would show like this:
$i = 0;
$content = array();
if(isset($_SESSION['cart'])){
foreach($_SESSION['cart'] as $result){
foreach($result as $item){
$values = $product->getById($item['id']);
if($values != null){ // which means it has product
/*
Checks if the array already contains that ID
this avoids duplicated products
*/
if(main::search_multidimensional($content, "id_product", $item['id_product']) == null)
$content[] = array("index" => $i, "quantity" => 1, $values);
else
$content[] = array("index" => $i, "quantity" => $result['quantity'] + 1, $values); // Retrieve current quantity and adds 1
$i++;
}
}
}
}
(I'm assuming you are only increasing the quantity by 1)
Solved.
All I had to do was to forget the $i variable, since it wasn't actually doing something necessary. Since I have id_product, which is unique I need to work with it.
if($values != null){ // Only if it has results
// checks if array already contains or not the product ID
// if does not have, it will add
if(global_::search_multidimensional($content, "id_product", $item['id_product']) == null){
$content[] = array("index" => $item['id_product'], "quantity" => 1, $values);
// index is now the id of the product
}else{
// otherwise, loop all the elements and add +1
foreach($content as $key => $result){
if($item['id_product'] == $content[$key]['index']){
$content[$key]['quantity']++;
}
}
}
}
As your $content array has fixed structure (fixed number of levels) you don't need to use recursive function. Your search_multidimensional function could be much simpler. And it should return index of found element (if any) in the array:
function search_multidimensional($array, $key, $value) {
foreach ($array as $i => $el) {
foreach ($el as $j => $v) {
if (is_int($j) && isset($v[$key]) && $v[$key] == $value) return $i;
}
}
return false;
}
So the snippet building $content should be changed like this:
...
if (($index = search_multidimensional($content, "id_product", $item['id_product'])) === false) {
$content[] = array("index" => $i, "quantity" => 1, $values); $i++;
}
else {
$content[$index]['quantity']++;
}
I was wondering when working with multimedional arrays, if a certain key is the same, is there a way to combine the contents of other keys into its own array if a certain key is the same?
Something like this:
// name is the same in both arrays
array(
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '1234567'
),
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '5556734'
)
)
into something like this
array(
array(
'name' => 'Pepsi',
'store' => array('Over here', 'Over here'),
'number' => array('1234567', '5556734')
)
)
The defining key is checking if the name element is the same for the other arrays.
You can try a function like this.
function mergeByKey($array,$key){
$tmp_array = array();
foreach ( $array as $k => $row ) {
$merged = false;
foreach ($tmp_array as $k2 => $tmp_row){
if ($row[$key] == $tmp_row[$key]){
foreach ( $row as $k3 => $value ) {
if ($k3 == $key) continue;
$tmp_array[$k2][$k3][] = $value;
$merged = true;
}
}
if ($merged) break;
}
if (!$merged) {
$new_row = array();
foreach ( $row as $k4 => $value ) {
if ($k4 == $key) $new_row[$k4] = $value;
else $new_row[$k4] = array($value);
}
$tmp_array[] = $new_row;
}
}
foreach ( $tmp_array as $t => $row ) {
foreach ( $row as $t2 => $value ) {
if ( count($value) == 1 && $t2 != $key ) $tmp_array[$t][$t2] = $value[0];
}
}
return $tmp_array;
}
passing the array as first parameter and the key as second one.
I'm referencing to your array structure
edited: missed a piece
edited2: if resultin array contains elements with one string, it returns a string and not a array with one element
demo
This function uses a given field name as the grouping identifier and turns all other fields into arrays.
Note that single occurrences of your field name will yield arrays with a single element for the other fields. I wasn't sure whether that's a desirable trait, but just making sure you know ;-)
$arr = array(
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '1234567'
),
array(
'name' => 'Pepsi',
'store' => 'Over here',
'number' => '5556734'
)
);
function mergeArray($array, $column)
{
$res = array();
foreach ($array as $item) {
foreach ($item as $key => $value) {
if ($key === $column) {
$res[$column][$key] = $value;
} else {
$res[$column][$key][] = $value;
}
}
}
return array_values($res);
}
print_r(mergeArray($arr, 'name'));
Demo
Thanks to Gianni Lovece for her answer but I was able to develop a much simpler solution based on this problem. Just plug in the $result_arr to browse through and the $key you want to use as basis and it immediately outputs a multidimensional array with non-repeating values for repeating elements (see example below).
function multiarray_merge($result_arr, $key){
foreach($result_arr as $val){
$item = $val[$key];
foreach($val as $k=>$v){
$arr[$item][$k][] = $v;
}
}
// Combine unique entries into a single array
// and non-unique entries into a single element
foreach($arr as $key=>$val){
foreach($val as $k=>$v){
$field = array_unique($v);
if(count($field) == 1){
$field = array_values($field);
$field = $field[0];
$arr[$key][$k] = $field;
} else {
$arr[$key][$k] = $field;
}
}
}
return $arr;
}
For example, in the sample array for this question, running multiarray_merge($mysample, 'name') returns
array(
'Pepsi' => array(
'name' => 'Pepsi',
'store' => 'Over here', // String: Not an array since values are not unique
'number' => array('1234567', '5556734') // Array: Saved as array since values are unique
)
);
I'm working on a leader board that pulls the top scorers into first, second, and third place based on points. Right now I'm working with a sorted array that looks like this (but could be of infinite length with infinite point values):
$scores = Array
(
["bob"] => 20
["Jane"] => 20
["Jill"] => 15
["John"] => 10
["Jacob"] => 5
)
I imagine I could use a simple slice or chunk, but I'd like to allow for ties, and ignore any points that don't fit into the top three places, like so:
$first = Array
(
["bob"] => 20
["Jane"] => 20
)
$second = Array
(
["Jill"] => 15
)
$third = Array
(
["John"] => 10
)
Any ideas?
$arr = array(
"Jacob" => 5,
"bob" => 20,
"Jane" => 20,
"Jill" => 15,
"John" => 10,
);
arsort($arr);
$output = array();
foreach($arr as $name=>$score)
{
$output[$score][$name] = $score;
if (count($output)>3)
{
array_pop($output);
break;
}
}
$output = array_values($output);
var_dump($output);
$first will be in $output[0], $second in $output[1] and so on.. Code is limited to 3 first places.
ps: updated to deal with tie on the third place
I would do something like:
function chunk_top_n($scores, $limit)
{
arsort($scores);
$current_score = null;
$rank = array();
$n = 0;
foreach ($scores as $person => $score)
{
if ($current_score != $score)
{
if ($n++ == $limit) break;
$current_score = $score;
$rank[] = array();
$p = &$rank[$n - 1];
}
$p[$person] = $score;
}
return $rank;
}
It sorts the array, then creates numbered groups. It breaks as soon as the limit has been reached.
You can do it with less code if you use the score as the key of the array, but the benefit of the above approach is it creates the array exactly how you want it the first time through.
You could also pass $scores by reference if you don't mind the original getting sorted.
Here's my go at it:
<?php
function array_split_value($array)
{
$result = array();
$indexes = array();
foreach ($array as $key => $value)
{
if (!in_array($value, $indexes))
{
$indexes[] = $value;
$result[] = array($key => $value);
}
else
{
$index_search = array_search($value, $indexes);
$result[$index_search] = array_merge($result[$index_search], array($key => $value));
}
}
return $result;
}
$scores = Array(
'bob' => 20,
'Jane' => 20,
'Jill' => 15,
'John' => 10,
'Jacob' => 5
);
echo '<pre>';
print_r(array_split_value($scores));
echo '</pre>';
?>