How to check the key of next index of arrays in PHP? - php

I have an PHP array :
$datas = array(
"abcd" => array(
"1639340364" => 1,
"1639362752" => 85,
"1639363500" => 74,
),
"efgh" => array(
"1639340364" => 78,
"1639362754" => 98,
"1639363500" => 46,
),
"ijkl" => array(
"1639340364" => 78,
"1639362754" => 98,
"1639363505" => 46,
),
);
I want to check the keys of each array and if not match need to add it on every array.
Expecting output like:
$datas = array(
"abcd" => array(
"1639340364" => 1,
"1639362752" => 85,
"1639362754" => 0,
"1639363500" => 74,
"1639363505" => 0,
),
"efgh" => array(
"1639340364" => 78,
"1639362752" => 0,
"1639362754" => 98,
"1639363500" => 46,
"1639363505" => 0,
),
"ijkl" => array(
"1639340364" => 78,
"1639362752" => 0,
"1639362754" => 98,
"1639363500" => 0,
"1639363505" => 46,
),
);
If the key is not exist need to add the key with value zero. Is that possible?
I tried with array_key_exists() function...
But I'm not sure where I want to check, when I'm checking It's return true(1). I know it's checking on the same array, Actually I want to know where I will check the condition?
foreach($datas as $key => $data ){
print_r($data);
foreach($data as $key => $point ){
$val = array_key_exists($key,$data);
echo $val;
}
}

I came up with this:
// prepare default keys
$keys = [];
foreach ($datas as $data) {
$keys = array_merge($keys, array_keys($data));
}
$keys = array_unique($keys);
sort($keys);
// construct default array with zeros
$default = array_combine($keys, array_fill(0, count($keys), 0));
// insert the default array
foreach ($datas as $key => $data) {
$datas[$key] = array_replace($default, $data);
}
// show result
echo '<pre>';
print_r($datas);
echo '</pre>';
I needed used a plethora of array functions. The idea is this: First I gather all the possible 'keys' from the array, make them unique, sort them and combine them into a new 'default' array containing only zero values. After that I use the array_replace() function to insert this array into the existing $datas array. This function does exactly what you want:
array_replace() replaces the values of array with values having the
same keys in each of the following arrays. If a key from the first
array exists in the second array, its value will be replaced by the
value from the second array.

It's not pretty or optimised, but here you go:
$subkeys = array();
foreach ($datas as $k => $d) {
foreach($d as $k2 => $d2) {
$subkeys[$k2] = 0;
}
}
foreach ($datas as $k => &$d) {
foreach ($subkeys as $xk => $xd) {
if ( ! array_key_exists($xk, $d)) {
$d[$xk] = $xd;
}
}
}

Related

Php multidimensional array filter and sum

i've a multidimensional array like this one:
['2021-04-01'=>
['hb' => 35, 'fb' => 40, 'ai' => 50],
'2021-04-02'=>
['hb' => 35, 'fb' => 40, 'ai' => 50],
'2021-04-03'=>
['hb' => 40, 'ai' => 55],
'2021-04-04'=>
['hb' => 40, 'fb' => 45, 'ai' => 55],
'2021-04-05'=>
['hb' => 35, 'ai' => 50]]
I'd like to receive an array like this one:
['hb'=>185,'ai'=>260]
Basically i've to sum the price of every single treatment (hb=half-board, fb=full board, ai=all inclusive) only if the treatment is present in every element of the array.
Basically the foreach loop is propably the best option. Although you can use array_sum function after array_map. Example for foreach:
$ret = []
foreach ($input_array as $second_array) {
foreach ($second_array as $key => $value) {
if (!isset($ret[$key]))
$ret[$key] = 0;
$ret[$key] += $value
}
}
Short solution with array_reduce().
$sum = array_reduce($arr ,function($carry,$val){
return ['hb'=>$carry['hb']+$val['hb'],'ai'=>$carry['ai']+$val['ai']];
},
['hb'=>0,'ai'=>0]
);
$arr is your input-array, $sum the result.
More understandable what array_reduce makes is a simple loop with foreach.
$sum = ['hb' => 0, 'ai' => 0];
foreach($arr as $row){
$sum['hb'] += $row['hb'];
$sum['ai'] += $row['ai'];
}

how to combine php array of object by unique id?

I have these 2 arrays.
First array is from user input $cart:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80],
['id' => 1, 'weight' => 50, 'percentage' => 80],
['id' => 2, 'weight' => 40, 'percentage' => 80],
];
and second array, I do a database SELECT id, stock WHERE id IN (3,1,2), resulting $db_item
$db_item = [
['id' => 1, 'stock' => 9539.00],
['id' => 2, 'stock' => 9468.00],
['id' => 3, 'stock' => 9295.00],
];
I want to add the stock attribute in second array to first array.
Expected output:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80, 'stock' => 9295.00],
['id' => 1, 'weight' => 50, 'percentage' => 80, 'stock' => 9539.00],
['id' => 2, 'weight' => 40, 'percentage' => 80, 'stock' => 9468.00],
];
This is what I tried, and it works, but I don't think it is necessary to have foreach, array_filter, and array_column:
foreach ($cart as $key => $cart_item) {
$item = array_filter($db_item, function($item) use ($cart_item) {
return $item['id'] === $cart_item['id'];
});
$cart[$key]['stock'] = array_column($item, 'stock')[0];
}
anyone has better idea how to optimize this?
EDIT: following Mohammad's answer, I can use more attribute in second array
$keys = [];
foreach ($arr2 as $item) {
$keys[$item['id']] = array(
'attr1' => $item['attr1'],
'attr2' => $item['attr2'],
// and so on
);
}
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
EDIT2: found out that we can simplify the foreach loop with just a single line using array_column.
$keys = array_column($arr2, null, 'id');
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
Use combination of array_flip() and array_column() to create array contain id and index of second array.
Then use array_map() to add new key stock to first array.
$keys = array_flip(array_column($arr2, 'id'));
$newArr = array_map(function($item) use($keys, $arr2){
$item['stock'] = $arr2[$keys[$item['id']]]['stock'];
return $item;
}, $arr1);
Check result in demo
Also you can use foreach instead of array_flip()
$keys = [];
foreach ($arr2 as $item)
$keys[$item['id']] = $item['stock'];
$newArr = array_map(function($item) use($keys){
$item['stock'] = $keys[$item['id']];
return $item;
}, $arr1);
Check result in demo
This can help too, one array_column + one array_map :
$arr2=array_column($arr2,'stock','id');
$arr1=array_map(function($val)use($arr2){$val['stock']=$arr2[$val['id']];return $val;},$arr1);

Filter/Remove rows where column value is found more than once in a multidimensional array

I need to remove rows from my input array where duplicate values occur in a specific column.
Sample array:
$array = [
['user_id' => 82, 'ac_type' => 1],
['user_id' => 80, 'ac_type' => 5],
['user_id' => 76, 'ac_type' => 1],
['user_id' => 82, 'ac_type' => 1],
['user_id' => 80, 'ac_type' => 5]
];
I'd like to filter by user_id to ensure uniqueness and achieve this result:
So, my output will be like this:
[
['user_id' => 82, 'ac_type' => 1],
['user_id' => 80, 'ac_type' => 5],
['user_id' => 76, 'ac_type' => 1]
]
I've already tried with:
$result = array_unique($array, SORT_REGULAR);
and
$result = array_map("unserialize", array_unique(array_map("serialize", $array)));
and
$result = array();
foreach ($array as $k => $v) {
$results[implode($v)] = $v;
}
$results = array_values($results);
print_r($results);
but duplicate rows still exist.
For a clearer "minimal, complete, verifiable example", I'll use the following input array in my demos:
$array = [
['user_id' => 82, 'ac_type' => 1],
['user_id' => 80, 'ac_type' => 5],
['user_id' => 76, 'ac_type' => 1],
['user_id' => 82, 'ac_type' => 2],
['user_id' => 80, 'ac_type' => 5]
];
// elements [0] and [3] have the same user_id, but different ac_type
// elements [1] and [4] have identical row data
Unconditionally push rows into a result array and assign associative first-level keys, then re-index with array_values(). This approach overwrites earlier duplicate rows with later occurring ones.
array_column demo:
var_export(array_values(array_column($array, null, 'user_id')));
foreach demo:
$result = [];
foreach ($array as $row) {
$result[$row['user_id']] = $row;
}
var_export(array_values($result));
Output:
[
['user_id' => 82, 'ac_type' => 2], // was input row [3]
['user_id' => 80, 'ac_type' => 5], // was input row [4]
['user_id' => 76, 'ac_type' => 1] // was input row [2]
]
Use a condition or the null coalescing assignment operator to preserve the first occurring row while removing duplicates.
foreach null coalescing assignment demo:
foreach ($array as $a) {
$result[$a['user_id']] ??= $a; // only store if first occurrence of user_id
}
var_export(array_values($result)); // re-index and print
foreach isset demo:
foreach ($array as $a) {
if (!isset($result[$a['user_id']])) {
$result[$a['user_id']] = $a; // only store if first occurrence of user_id
}
}
var_export(array_values($result)); // re-index and print
Output:
[
['user_id' => 82, 'ac_type' => 1], // was input row [0]
['user_id' => 80, 'ac_type' => 5], // was input row [1]
['user_id' => 76, 'ac_type' => 1] // was input row [2]
]
It is also possible to unconditionally push data AND avoid a condition, but the row order may differ between the input and output (if it matters to you).
array_reverse, array_column demo:
var_export(array_values(array_column(array_reverse($array), null, 'user_id')));
array_reduce demo:
var_export(
array_values(
array_reduce(
$array,
fn($res, $row) => array_replace([$row['user_id'] => $row], $res),
[]
)
)
);
foreach array_reverse demo:
$result = [];
foreach (array_reverse($array) as $row) {
$result[$row['user_id']] = $row;
}
var_export(array_values($result));
Output:
[
['user_id' => 80, 'ac_type' => 5], // was input row [1]
['user_id' => 82, 'ac_type' => 1], // was input row [0]
['user_id' => 76, 'ac_type' => 1] // was input row [2]
]
A warning about a fringe case not expressed in this example: if you are using row values as identifiers that may be corrupted upon being used as keys, the above techniques will give unreliable results. For instance, PHP does not allow float values as keys (they will cause an error or be truncated, depending on your PHP version). Only in these fringe cases might you consider using inefficient, iterated calls of in_array() to evaluate uniqueness.
Using array_unique(..., SORT_REGULAR) is only suitable when determining uniqueness by ENTIRE rows of data.
array_unique demo:
var_export(array_unique($array, SORT_REGULAR));
Output:
[
['user_id' => 82, 'ac_type' => 1], // was input row [0]
['user_id' => 80, 'ac_type' => 5], // was input row [1]
['user_id' => 76, 'ac_type' => 1] // was input row [2]
['user_id' => 82, 'ac_type' => 2], // was input row [3]
]
As a slight extension of requirements, if uniqueness must be determined based on more than one column, but not all columns, then use a "composite key" composed of the meaningful column values. The following uses the null coalescing assignment operator, but the other techniques from #2 and #3 can also be implemented.
Code: (Demo)
foreach ($array as $row) {
$compositeKey = $row['user_id'] . '_' . $row['ac_type'];
$result[$compositeKey] ??= $row; // only store if first occurrence of compositeKey
}
$array = [
['user_id'=>82,'ac_type'=>1],
['user_id'=>80,'ac_type'=>5],
['user_id'=>76,'ac_type'=>1],
['user_id'=>82,'ac_type'=>2],
['user_id'=>80,'ac_type'=>6]
];
$array = array_reverse($array);
$v = array_reverse(
array_values(
array_combine(
array_column($array, 'user_id'),
$array
)
)
);
echo '<pre>';
var_dump($v);
Result:
array(3) {
[0]=>
array(2) {
["user_id"]=>
int(76)
["ac_type"]=>
int(1)
}
[1]=>
array(2) {
["user_id"]=>
int(82)
["ac_type"]=>
int(1)
}
[2]=>
array(2) {
["user_id"]=>
int(80)
["ac_type"]=>
int(5)
}
}
Took me a while, but this should work (explanation in comments):
<?php
/* Example array */
$result = array(
0 => array(
"user_id" => 82,
"ac_type" => 1
),
1 => array(
"user_id" => 80,
"ac_type" => 5
),
2 => array(
"user_id" => 76,
"ac_type" => 1
),
3 => array(
"user_id" => 82,
"ac_type" => 2
),
4 => array(
"user_id" => 80,
"ac_type" => 2
)
);
/* Function to get the keys of duplicate values */
function get_keys_for_duplicate_values($my_arr, $clean = false) {
if ($clean) {
return array_unique($my_arr);
}
$dups = $new_arr = array();
foreach ($my_arr as $key => $val) {
if (!isset($new_arr[$val])) {
$new_arr[$val] = $key;
} else {
if (isset($dups[$val])) {
$dups[$val][] = $key;
} else {
//$dups[$val] = array($key);
$dups[] = $key;
// Comment out the previous line, and uncomment the following line to
// include the initial key in the dups array.
// $dups[$val] = array($new_arr[$val], $key);
}
}
}
return $dups;
}
/* Create a new array with only the user_id values in it */
$userids = array_combine(array_keys($result), array_column($result, "user_id"));
/* Search for duplicate values in the newly created array and return their keys */
$dubs = get_keys_for_duplicate_values($userids);
/* Unset all the duplicate keys from the original array */
foreach($dubs as $key){
unset($result[$key]);
}
/* Re-arrange the original array keys */
$result = array_values($result);
echo '<pre>';
print_r($result);
echo '</pre>';
?>
Function was taken from this the answer to this question: Get the keys for duplicate values in an array
Output:
Array
(
[0] => Array
(
[user_id] => 82
[ac_type] => 1
)
[1] => Array
(
[user_id] => 80
[ac_type] => 5
)
[2] => Array
(
[user_id] => 76
[ac_type] => 1
)
)
Tested and working example.
<?php
$details = array('0'=> array('user_id'=>'82', 'ac_type'=>'1'), '1'=> array('user_id'=>'80', 'ac_type'=>'5'), '2'=>array('user_id'=>'76', 'ac_type'=>'1'), '3'=>array('user_id'=>'82', 'ac_type'=>'1'), '4'=>array('user_id'=>'80', 'ac_type'=>'5'));
function unique_multidim_array($array, $key) {
$temp_array = array();
$i = 0;
$key_array = array();
foreach($array as $val) {
if (!in_array($val[$key], $key_array)) {
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
$i++;
}
return $temp_array;
}
?>
<?php
$details = unique_multidim_array($details,'user_id');
?>
<pre>
<?php print_r($details); ?>
</pre>
Will output:
Array
(
[0] => Array
(
[user_id] => 82
[ac_type] => 1
)
[1] => Array
(
[user_id] => 80
[ac_type] => 5
)
[2] => Array
(
[user_id] => 76
[ac_type] => 1
)
)
taken from here http://php.net/manual/en/function.array-unique.php in the user contributed notes.

PHP array_reduce with initial parameter as an array

I have this initial array:
[
0 => ['id' => 5, 'value' => 50],
1 => ['id' => 6, 'value' => 60],
2 => ['id' => 7, 'value' => 70],
]
and want to convert it to:
[
5 => ['value' => 50],
6 => ['value' => 60],
7 => ['value' => 70],
]
At first, I tried to use map, but it can't modify the array keys, so I thought reduce would solve the problem because it reduces the array to a single value, in this case, an array. So I tried:
array_reduce(
$array,
function($carry, $item) {
return $carry[$item['id']] = $item['value'];
},
[]
);
But it returns this error Cannot use a scalar value as an array. What am I doing wrong? Does array_reduce cannot receive an array as an initial value?
Your array_reduce didn't work because You weren't returning the accumulator array (carry in Your case) from the callback function.
array_reduce(
$array,
function($carry, $item) {
$carry[$item['id']] = $item['value'];
return $carry; // this is the only line I added :)
},
[]
);
I came to this question while looking for a way to use array_reduce, so I felt I should write this comment. I hope this will help future readers. :)
As Mark Bakerdid it. I also did with foreach loop.
$arr = array(
array('id' => 5, 'value' => 50),
array('id' => 6, 'value' => 60),
array('id' => 7, 'value' => 70)
);
$result = array();
$result = array_column($arr, 'value', 'id');
array_walk($result, function(&$value) { $value = ['value' => $value]; });
//I did this using foreach loop, But the OP need it through array function.
//foreach($arr as $key => $value){
// $result[$value['id']] = array('value' => $value['value']);
//}
echo '<pre>';
print_r($result);
Result:
Array
(
[5] => Array
(
[value] => 50
)
[6] => Array
(
[value] => 60
)
[7] => Array
(
[value] => 70
)
)
Sometimes the best solutions are the simplest. Loop through your array and assign the id and value to a new array.
$new_array = array();
foreach ($array as $key => $arr) {
$new_array[$arr['id']] = array('value' => $arr['value']);
}
You can do it functionally. I suspect it's not actually more readable however.
array_combine(
array_column($a, 'id'),
array_map(function($v) { return ['value' => $v['value']]; }, $a)
);
Or even...
array_map(
function($v) { return ['value' => $v['value']]; },
array_column($a, null, 'id')
)
array_reduce($ar, function ($acc, $item) {
$acc[$item['id']] = [ 'value' => $item['value']];
return $acc;
}, [])

count key length php

I can't find anywhere how to make an array with the keys and the character length of that key.
Example:
<?php
$array = array("ab" => 145, "bassfasdf" => 13, "cdfsa" => 2684);
print_r($array);
?>
I would like it to output this:
Array
(
[ab] => 2
[bassfasdf] => 9
[cdfsa] => 5
)
Does anyone have any idea on how to do that?
$array = array("ab" => 145, "bassfasdf" => 13, "cdfsa" => 2684);
foreach ($array as $key => &$value) {
$value = strlen($key);
}
Might be a shorter way:
$result = array_combine($keys=array_keys($array), array_map('strlen', $keys));
You could use an array_map function, or a foreach loop as shown below:
$array = array("ab" => 145, "bassfasdf" => 13, "cdfsa" => 2684);
$keyLengths = array();
foreach ($array as $key=>$value)
{
$keyLengths[$key] = strlen($key);
}

Categories