How to duplicate array and sum up those array values in PHP - php

I have values 1,2,3 and it will create another value like below and produce results by summing. As there are 3 values so that it will create three arrays. if it were 5 values then 5 such arrays are required.
1 2 3
1 2 3
1 2 3
Result is : 1 3 6 5 3
What I am doing is :
$a=[1,2,3];
$b=$a;
$c=$a;
$d=[];
array_push($a,0,0);
array_unshift($b,0);
array_push($b,0);
array_unshift($c,0,0);
$d = array_map(function () {
return array_sum(func_get_args());
}, $a,$b,$c);
print_r($d);
I am not able to find the way to do this for more values than 3 and dynamically. So that I have to just put the values and it gives me the result. I am not asking for the code but you can help me with that how I should approach it. Thanks.

I recommend a linear approach with no preparations or padding or data bloat.
Use the indexes and simple arithmetic to add values to their desired element in the output.
Code: (Demo)
$array = range(1, 3);
$result = [];
foreach ($array as $shifter => $unused) {
foreach ($array as $index => $value) {
$key = $shifter + $index;
$result[$key] = ($result[$key] ?? 0) + $value;
}
}
var_export($result);
// [1, 3, 6, 5, 3]
This is very clean, readable, maintainable, and most efficient.
While $shifter = 0, $key will be 0, 1 then 2; forming [1, 2, 3].
While $shifter = 1, $key will be 1, 2 then 3; forming [1, 3, 5, 3].
While $shifter = 2, $key will be 2, 3 then 4; forming [1, 3, 6, 5, 3].

If you're using PHP 7.4+ you can use the below...
// Ensure the keys are indexed not associative.
$input = array_values( [ 1, 2, 3, 4, 5 ] );
// Create rows.
$rows = [];
foreach ( $input as $key => $value ) {
$rows[ $key ] = array_merge( array_fill( 0, $key, 0 ), $input );
}
// Sum values.
$output = array_map( function() {
return array_sum( func_get_args() );
}, ...$rows );
If you're using a PHP version below 7.4 you can do...
// Ensure the keys are indexed not associative.
$input = array_values( [ 1, 2, 3 ] );
// Create rows.
$rows = [];
foreach ( $input as $key => $value ) {
$rows[ $key ] = array_merge( array_fill( 0, $key, 0 ), $input );
}
// Sum values.
$output = call_user_func_array( 'array_map', array_merge( [ function() {
return array_sum( func_get_args() );
} ], $rows ) );

I'm thinking using an offset makes sense, it's the same as your approach, except I'm adding a 0 using array_fill
$arr = [1,2,3];
$result = [];
$offset = 0;
for($i = 0; $i < count($arr); $i++) {
$result = array_map(function () {
return array_sum(func_get_args());
}, $result, array_merge(array_fill(0, $offset, 0), $arr));
$offset++;
}
var_dump($result);
The advantage of using this is that it uses very little memory even for a very large initial array.

Related

PHP array_unique is not returning unique values [duplicate]

My array is :
$array= array(4,3,4,3,1,2,1);
And I'd like to output it like below:
Output = 2
(As 2 is present only once)
This is what I've tried:
$array = array(4, 3, 4, 3, 1, 2, 1);
$array1 = array(4, 3, 4, 3, 1, 2, 1);
$array_diff = array_diff($array, $array1);
*Read last section of this an for the most stable technique the avoids fringe case issues -- it is also the most verbose.
One-liner with no loops: (Demo)
var_export(array_keys(array_intersect(array_count_values($array),[1])));
The breakdown:
array_keys( // return the remaining keys from array_count_values
array_intersect( // filter the first array by second
array_count_values($array), // count number of occurrences of each value
[1] // identify the number of occurrences to keep
)
)
if you (or any future reader) wants to keep more values, replace the second parameter/array in array_intersect().
for instance:
you want to keep 1,2,and 3: array(1,2,3) or [1,2,3]
p.s. For the record, you can use array_filter() with a custom function to omit all non-1 count values, but I have used array_intersect() because the syntax is more brief and IMO easier to read.
p.s. thought I'd revisit and include a PHP7.4 technique and compare against other function-based techniques...
Code: (Demo)
$numbers = [4, 3, 4, 3, 1, 2, 1];
var_export(
array_keys(
array_intersect(
array_count_values($numbers),
[1]
)
)
);
echo "\n---\n";
var_export(
array_keys(
array_filter(
array_count_values($numbers),
function($count) {
return $count === 1;
}
)
)
);
echo "\n---\n";
// PHP7.4+
var_export(
array_keys(
array_filter(
array_count_values($numbers),
fn($count) => $count === 1
)
)
);
*For similar tasks which have values which are not guaranteed to be integers, array_count_values() will complain with "Warning: array_count_values(): Can only count string and integer values".
Even a classic loop that uses values as first level keys like #LF00's answer will suffer potential side effects due to floats and numeric values being cast as integers automatically.
This means that a more general-use solution would be: (Demo)
$result = [];
foreach ($array as $index => $value) {
foreach ($array as $i => $v) {
if ($value === $v && $index !== $i) {
continue 2; // duplicate found, stop checking this value; do not retain
}
}
$result[] = $value;
}
var_export($result);
You could use the array_count_values() php function.
For example:
$numbers = [4, 3, 4, 3, 1, 2, 1];
// build count array as key = number and value = count
$counter_numbers = array_count_values($numbers);
print_r($counter_numbers);
Output :
Array
(
[4] => 2
[3] => 2
[1] => 2
[2] => 1
)
Then loop through the new array to get non-repeated values :
$unique_numbers = [];
foreach ($counter_numbers as $number => $count) {
if ($count === 1) {
$unique_numbers[] = $number;
}
}
print_r($unique_numbers);
Output :
Array
(
[0] => 2
)
You can do it like this: Count the occurrences of each element, then filter out the occurrences bigger than 1.
$array = [4, 3, 4, 3, 1, 2, 1];
foreach ($array as $v)
{
$arr[$v][] = 1; // doesn't matter if 1 is set to a different value
}
foreach($arr as $k => $v)
{
if (count($v) == 1) {
$o[] = $k;
}
}
print_r($o);
result:
Array
(
[0] => 2
)
If in your scenario there will be only one unique value you could use:
$array= array(4,3,4,3,1,2,1);
$singleValue = array_search(1, array_count_values($array));
var_dump($singleValue) // Outputs: 2

Combine two arrays but add Values of duplicate keys together

I have two arrays, $ids and $quants (ids and quantities of stock items) that need to be combined, but instead of replacing or removing duplicates, their values should be added together.
Currently I'm using array_combine() but this means that some of the quantities are lost where multiple of the same id exists.
e.g.
$ids = Array(1, 1, 2, 3);
$quants = Array(10, 20, 30, 40);
Desired output:
$combined = Array(
[1] => 30
[2] => 30
[3] => 40
)
Thanks in advance for any advice
$ids = Array(1, 1, 2, 3);
$quants = Array(10, 20, 30, 40);
$a = array_unique($ids);
$a = array_combine($a, array_fill(0, count($a), 0));
foreach($ids as $k=>$v) {
$a[$v] += $quants[$k];
}
print_r($a);
There isn't a built in function, so you have to do it yourself:
function my_array_combine($keys, $values)
{
if (count($keys) != count($values)) {
throw new InvalidArgumentException('More or less');
}
$result = array();
$values = array_values($values); // make sure it is indexed 0, 1, 2
foreach(array_values($keys) as $idx => $key) {
// Correspondending value is at $values[$idx];
if (isset($result[$key])) {
$result[$key] += $values[$idx];
} else {
$result[$key] = $values[$idx];
}
}
return $result;
}

How to split php array into two arrays following some condition?

I want to split an array according to sub_id
my main array:
$my_main_array = [
['id' => 1, 'sub_id' => 8],
['id' => 2, 'sub_id' => 9],
['id' => 3, 'sub_id' => 8],
['id' => 3, 'sub_id' => 9],
['id' => 3, 'sub_id' => 9],
];
The result should be,The first array:
sub_id equal 8
sub_id equal 8
The second array
sub_id equal 9
sub_id equal 9
sub_id equal 9
$arrayA = [];
$arrayB = [];
array_map(function($value) use (&$arrayA, &$arrayB) {
if ($value['sub_products_id'] == 8) {
$arrayA[] = $value;
} else {
$arrayB[] = $value;
}
}, $originalArray);
I am a bit late, but I was facing this problem today, so I thought I could let my solution here because maybe someone find it useful.
You can let this function created:
function array_split($original_array, $callback) {
$return = [[],[]]; // initialize both arrays we will use as output
foreach( $original_array as $item ) {
// Add each item to the corresponding array, according to the callback output
$return[$callback($item)][] = $item;
}
return $return;
}
and then use it like this:
[$sub_id_9_array, $sub_id_8_array] = array_split(
$my_main_array,
function($item) { return $item['sub_id'] == 8;}
);
Use array_filter() with a callback that matches against your condition
$key = 8;
$first = array_filter(
$originalArray,
function($value) use ($key) {
return $value['sub_products_id'] == $key;
}
);
$key = 9;
$second = array_filter(
$originalArray,
function($value) use ($key) {
return $value['sub_products_id'] == $key;
}
);

Merge two flat arrays and omit values from one array when the other array has more occurrences of the same value

I want to merge every element of two arrays, BUT if a value is in both arrays, then only add the values from the array which has the biggest amount of that element. The result array does not need to be sorted in any special way, but I did it here for readability.
Sample input:
$array1 = [1, 4, 7, 3, 3, 3];
$array2 = [4, 0, 3, 4, 9, 9];
Desired result:
[0, 1, 3, 3, 3, 4, 4, 7, 9, 9]
//a2 a1 a1 a1 a1 a2 a2 a1 a2 a2
Note, this will be used on big arrays, with unknown integer values. Is there a good way to do this that doesn't require too much time/processing power?
Try this:
<?php
$array1 = [1, 4, 7, 3, 3, 3];
$array2 = [4, 0, 3, 4, 9, 9];
function min_merge($arr1, $arr2) {
$arr1 = array_count_values($arr1);
$arr2 = array_count_values($arr2);
foreach ($arr2 as $index => $arr)
if (!isset($arr1[$index]) || $arr > $arr1[$index])
$arr1[$index] = $arr;
foreach ($arr1 as $index => $arr)
for ($i = 0; $i < $arr; $i++)
$final[] = $index;
return $final;
}
print_r(min_merge($array1, $array2));
Output:
Array (
[0] => 1
[1] => 4
[2] => 4
[3] => 7
[4] => 3
[5] => 3
[6] => 3
[7] => 0
[8] => 9
[9] => 9
)
Unsorted, but it contains all the numbers from [0, 1, 3, 3, 3, 4, 4, 7, 9, 9].
$count[0] = array_count_values($arr1);
$count[1] = array_count_values($arr2);
$out = array();
array_map(function($e) use(&$out, $count){
$n1 = (isset($count[0][$e])) ? $count[0][$e] : 0;
$n2 = (isset($count[1][$e])) ? $count[1][$e] : 0;
$next = ($n2 > $n1) ? array_fill(0, $n2, $e) : array_fill(0, $n1, $e);
$out = array_merge($out, $next);
}, array_keys($count[0] + $count[1]));
print_r($out);
My modernized rewrite of #DaveChen's answer using PSR-12 coding standards and eliminating single-use declarations. This approach uses one loop to determine the greater count for numbers shared by both value-count arrays, then a second loop to populate the result array. (Demo)
$counts1 = array_count_values($array1);
foreach (array_count_values($array2) as $number => $count) {
if ($count > ($counts1[$number] ?? 0)) {
$counts1[$number] = $count;
}
}
$result = [];
foreach ($counts1 as $number => $count) {
array_push($result, ...array_fill(0, $count, $number));
}
var_export($result);
My modernized rewrite of #Expedito's answer which does not abuse the array_map() (when array_map()'s return value is not used, use array_walk() for functional style programming), uses a foreach() loop to eliminate variable scope issues, and generally implements D.R.Y. techniques. (Demo)
$counts1 = array_count_values($array1);
$counts2 = array_count_values($array2);
$result = [];
foreach ($counts1 + $counts2 as $num => $cnt) {
array_push(
$result,
...array_fill(
0,
max($counts1[$num] ?? 0, $counts2[$num] ?? 0),
$num
)
);
}
var_export($result);
And I wanted to add a new approach of my own, despite the fact that it may or may not perform better than the other two snippets. The script makes one pass over the first value count arrays to populate a temporary array which demands which numbers from the first array should be represented in the result array. Then it isolates value intersections from the first array, value differences from the second array, then merges them. (Demo)
$counts1 = array_count_values($array1);
$counts2 = array_count_values($array2);
$keepFrom1 = array_keys(
array_filter(
$counts1,
fn($count, $number) => ($counts2[$number] ?? 0) <= $count,
ARRAY_FILTER_USE_BOTH
)
);
var_export(
array_merge(
array_intersect($array1, $keepFrom1),
array_diff($array2, $keepFrom1)
)
);
probably not the most optimized but
<?php
$one=[1, 4, 7, 3, 3, 3];
$two=[4, 0, 3, 4, 9, 9];
sort($one);
sort($two);
foreach($one as $el)
{
$combined[]=$el;
if (array_search($el,$two))
{
unset($two[array_search($el,$two)]);
}
}
foreach($two as $el)
{
$combined[]=$el;
}
sort($combined);
print_r($combined);
?>

Sum all values in a 2d array

How can I sum all of the values in an array of arrays?
Sample array:
[
[0],
[0],
[1, 6, 0]
]
Desired output: 7
echo array_sum(
call_user_func_array('array_merge', $array)
);
//or
echo array_sum(
array_map('array_sum', $array)
);
Use recursion. This will sum an array to any depth (ignoring php imposed call stack depth limit):
function sum_all($arr)
{
$sum = 0;
foreach ($arr as $val)
{
if (is_array($val))
$sum += sum_all($val);
else
$sum += $val;
}
return $sum;
}
I guess those three arrays are actually all part of a top-level array, like this:
$array = array( array( 0 ), array( 1, 2, 3, 4 ), array( 5, 6 ) );
$sum = 0;
for ( $i = 0; $i < count( $array ); $i++ )
{
$sum += array_sum( $array[$i] );
}

Categories