PHP array reference issue with ?? operator - php

<?php
$all_stock_data = [
"kit_38320" => [
"law" => [
1724 => [
'id' => 320044,
'reserved' => 4
]
]
]
];
foreach($all_stock_data["kit_38320"]["law"] ?? [] as &$section_stock){
$section_stock['reserved'] += 1;
}
echo "<pre>";
print_r($all_stock_data);
if a use "?? []" in foreach reserved count is not increase.
who can explain this?
if(empty($all_stock_data["kit_38320"]["law"])){
$all_stock_data["kit_38320"]["law"] = [];
}
foreach($all_stock_data["kit_38320"]["law"] as &$section_stock){
$section_stock['reserved'] += 1;
}
Thank u for answers. I think its better way

(not sure of the terminology but) It is effectively creating a copy of the value as
$all_stock_data["kit_38320"]["law"] ?? []
is an expression, so the foreach is operating over the result of the expression and not the $all_stock_data array.
If you add some extra displays to show the values...
foreach($all_stock_data["kit_38320"]["law"] ??[] as &$section_stock){
print_r($section_stock);
$section_stock['reserved'] += 1;
print_r($section_stock);
}
you get the value is being updated
Array
(
[id] => 320044
[reserved] => 4
)
Array
(
[id] => 320044
[reserved] => 5
)
but this isn't in the $all_stock_data array.

Related

Merge multiple multidimensional arrays by value

Problem
I have the following array, consisting of N different services, where each entry consists of an identifier and a unique (user)name.
$input = [
'service_1' => [
'1234' => 'John_Doe_1',
'4567' => 'Jane Doe X',
'7891' => 'J.Doe1',
],
'service_2' => [
'100001' => 'Jane Doe X',
'100002' => 'John_Doe_1',
'100003' => 'J.Doe1',
],
'service_N' => [
'07faed21-2920-4d7d-a263-88deba9c422c' => 'John_Doe_1',
'1160178c-dfbf-4091-b4c0-a8ec55c22800' => 'J.Doe1',
],
];
Now I'm looking for a way to format it in a way that I get the identifiers across each (user)name for the different services:
$output = [
'John_Doe_1' => [
'service_1' => '1234',
'service_2' => '100002',
'service_N' => '07faed21-2920-4d7d-a263-88deba9c422c',
],
'Jane Doe X' => [
'service_1' => '4567',
'service_2' => '100001',
'service_N' => null, // either value should be null or key should not exist
],
'J.Doe1' => [
'service_1' => '7891',
'service_2' => '100003',
'service_N' => '1160178c-dfbf-4091-b4c0-a8ec55c22800',
],
];
I'm looking for a flexible way (with N services) to do this but I can't come up with a good solution.
The code below should do the trick. This is how it works.
Loops over the keys of the outer array to get the keys for the output array.
Loops over the key value pair within every inner array.
Creates an empty array in the output array with the username as key if it does not exist.
Adds the 'ID' of the server under the name that we created earlier under the key it's currently looping through.
This should also still work within a reasonable time if your array input gets really big (e.g. 10000 elements).
$output = [];
// Loop over service_1, service_2, service_N etc.
foreach(array_keys($input) as $service_name)
{
// Loop over the inner key value pair (e.g. 10001 => John Doe X)
foreach($input[$service_name] as $service_id => $username)
{
// Create a key with the name if it does not exist in the output.
if(!isset($output[$username]))
{
$output[$username] = [];
}
// Add the key value pair to the correct output name.
$output[$username][$service_name] = $service_id;
}
}
That code will produce the following output.
Array
(
[John_Doe_1] => Array
(
[service_1] => 1234
[service_2] => 100002
[service_N] => 07faed21-2920-4d7d-a263-88deba9c422c
)
[Jane Doe X] => Array
(
[service_1] => 4567
[service_2] => 100001
)
[J.Doe1] => Array
(
[service_1] => 7891
[service_2] => 100003
[service_N] => 1160178c-dfbf-4091-b4c0-a8ec55c22800
)
)
I've been on a functional programming kick recently and figured I'd dive into PHP to see what I could come up with. Here's a nested array_walk method that seems to do the trick!
$output = Array();
array_walk($input, function($item, $key) use (&$output) {
array_walk($item, function($item, $key, $parent_key) use (&$output) {
$output[$parent_key][$item] = $key;
}, $key);
});
var_dump($output);

How to convert PHP arrays?

This is an array of PHP database output:
[
[hi]=>array(
[text] => 'ok'
)
[work]=>array(
[text] => 'usa'
)
[city]=>array(
[text] => 'newyork'
)
]
How do I convert it to look like this:
[
[hi]=> 'ok'
[work]=> 'usa'
[city]=> 'newyork'
]
You just want to loop over the array and build your newly formatted array.
<?php
$arr = [
'please' => [
'text' => 'it'
],
'use' => [
'text' => 'will'
],
'google' => [
'text' => 'help'
]
];
$formattedArr = [];
foreach ($arr as $k => $v) {
$formattedArr[$k] = $v['text'];
}
Another way using some array sorting methods to get us a one liner:
$formattedArr = array_combine( array_keys($arr), array_column($arr, 'text'));
array_combine is a php function that creates a new array using an array for keys and an array for values.
Simply enough, array_keys gets - you guessed it - the keys of the array.
array_column creates an array of a selected property from an inner array, so in this case, it goes plucks the 'text' values from the inner arrays.

From a 2D array, how to create groups (i.e. arrays) of inner arrays which have the same value for a particular field?

I have the following array:
fruits = [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"green", "name"=>"kiwi"],
3 => ["color"=>"red", "name"=>"carrot"],
4 => ["color"=>"yellow", "name"=>"banana"],
5 => ["color"=>"yellow", "name"=>"mango"],
];
And I need to get it into the following form:
fruits = [
0 => [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"red", "name"=>"carrot"]
],
1 => [
0 => ["color"=>"yellow", "name"=>"banana"],
1 => ["color"=>"yellow", "name"=>"mango"],
],
2 => [
0 => ["color"=>"green", "name"=>"kiwi"],
]
];
That is I need to group on the basis of color of the fruit.
I tried but somehow I can not get it correct.
An associative array would be simple :
$sortedFruits = array();
for ($i = 0; $i < count($fruits); $i++) {
if(!$sortedFruits[$fruits[$i]['color']]) { $sortedFruits[$fruits[$i]['color']] = array(); }
array_push($sortedFruits[$fruits[$i]['color']], $fruits[$i]);
}
print_r($sortedFruits['red']);
One option is to loop the colors by extracting names and colors to seperate flat arrays.
This way you only loop the count of unique colors (in this case three times).
Then you do the matching with array_intersect.
$colors = array_column($fruits, "color");
$names = array_column($fruits, "name");
foreach(array_unique($colors) as $color) $new[$color] = array_intersect_key($names, array_intersect($colors, [$color]));
var_dump($new);
https://3v4l.org/5pODc
You're overcomplicating the resulting array, I would suggest to replace first level key with color name and just go with names in each of them.
Building a code logic for your example will be way to big and it doesn't need to be.

How to sort LDAP Result with LDAP filter?

I'm creating a LDAP directory search using PHP.
I'm able to successfully search and return results.
I want to be able to sort results according to hierarchy.
Now I have:
ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=IT,ou=Employees,ou=People,dc=instatsport,dc=com
ou=Video,ou=Employees,ou=People,dc=instatsport,dc=com
ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=aHR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=HR2,ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
Using php.net/manual/en/function.ldap-sort.php what filter should be applied to get this result?:
ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=HR2,ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=aHR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
ou=IT,ou=Employees,ou=People,dc=instatsport,dc=com
ou=Video,ou=Employees,ou=People,dc=instatsport,dc=com
You can't with ldap_sort.
What this function do is apply an strcmp on an attribute of the entries returned.
Here you try to order the DN of the entries returned, and not on an alphabetical way, but on the hierarchical level.
You have to implement this (or find someone who already did this).
The easiest implementation would be to create a tree with all the RDN and print this tree from root to leaf recursively imploding each RDN with a comma.
EDIT :
I took some time to try to implement it quickly, this is not the best code you can find, but it can ba a base for what you want to achieve :
function hierarchySort ($a, $b){
$rdn1 = ldap_explode_dn($a,0);
$rdn2 = ldap_explode_dn($b,0);
$count = $rdn1["count"] < $rdn2["count"] ? $rdn1["count"] : $rdn2["count"];
for ($i=0; $i<$count; $i++) {
$j1 = $rdn1["count"] - 1 - $i;
$j2 = $rdn2["count"] - 1 - $i;
$ret = strnatcasecmp($rdn1[$j1], $rdn2[$j2]);
if ($ret != 0) break;
}
if ($rdn1["count"] == $rdn2["count"]) {
return $ret;
}else {
if ($ret != 0) return $ret;
else return $rdn1["count"] < $rdn2["count"] ? -1 : 1;
}
}
// Some simulated LDAP result
$entries = [
0 => [ "dn" => "ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com" ],
1 => [ "dn" => "ou=IT,ou=Employees,ou=People,dc=instatsport,dc=com" ],
2 => [ "dn" => "ou=Video,ou=Employees,ou=People,dc=instatsport,dc=com" ],
3 => [ "dn" => "ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com" ],
4 => [ "dn" => "ou=aHR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com" ],
5 => [ "dn" => "ou=HR2,ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com" ],
"count" => 6
];
for ($i=0; $i<$entries['count']; $i++) {
$e = $entries[$i];
$dns[] = $e["dn"];
}
print_r($dns);
usort($dns, 'hierarchySort');
print_r($dns);
Ouput :
Array
(
[0] => ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[1] => ou=IT,ou=Employees,ou=People,dc=instatsport,dc=com
[2] => ou=Video,ou=Employees,ou=People,dc=instatsport,dc=com
[3] => ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[4] => ou=aHR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[5] => ou=HR2,ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
)
Array
(
[0] => ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[1] => ou=aHR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[2] => ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[3] => ou=HR2,ou=HR1,ou=HR,ou=Employees,ou=People,dc=instatsport,dc=com
[4] => ou=IT,ou=Employees,ou=People,dc=instatsport,dc=com
[5] => ou=Video,ou=Employees,ou=People,dc=instatsport,dc=com
)
ldap_sort is deprecated by now as it only sorts the result set returned by the server. For small result sets that might not be an issue, but on larger (paged) result sets that means that you get results from "a" to "z" on the first page which will then be sorted. And on the second page you will also get results from "a" to "z" that will then be sorted and so on. Therefore the sort does not really what most people expect.
Also ldap_sort can only sort on fields that where actually retrieved. So you are not able to only retrieve the "first name" and sort by "second name".
To answer your question: I would write a sort function like the following and use that with f.e. usort:
function sortByDn($a, $b) {
return strnatcasecmp($a['dn'], $b['dn']);
}
$result = ldap_get_Entries($handle, $resulthandle);
unset($result['count']);
usort($result, 'sortByDn');
Something along these lines might actually help ;)
Depending on what you want to sort by your sort function might need to be different!

Remove specific records from associative array key in php

Hi I have an array that contains two arrays that has the following structure:
categories [
"lvl0" => array:2 [
0 => "Cleaning"
1 => "Bread"
]
"lvl1" => array:2 [
0 => null
1 => "Bread > rolls"
]
]
I would like to remove any records of NULL from the 'lvl1' array but have not been able to find the correct method to do this.
I have tried:
array_filter($categories['lvl1'])
But this also removes all records associated to lvl1 and not just the NULL ones.
Any help would be greatly appreciated.
Thanks
array_filter() takes a callback as the second argument. If you don't provide it, it returns only records that aren't equal to boolean false. You can provide a simple callback that removes empty values.
array_filter() also uses a copy of your array (rather than a reference), so you need to use the return value.
For instance:
$categories = [
"lvl0" => [
"Cleaning",
"Bread"
],
"lvl1" => [
null,
"Bread > rolls"
]
];
$lvl1 = array_filter($categories['lvl1'], function($value) {
return !empty($value);
});
var_dump($lvl1);
That will return:
array(1) {
[1] =>
string(13) "Bread > rolls"
}
I was having the same issue on my last working day.Generally for associative array array_filter() needs the array key to filter out null, false etc values. But this small function help me to filter out NULL values without knowing the associative array key. Hope this will also help you, https://eval.in/881229
Code:
function array_filter_recursive($input)
{
foreach ($input as &$value)
{
if (is_array($value))
{
$value = array_filter_recursive($value);
}
}
return array_filter($input);
}
$categories = [
"lvl0" => [
"Cleaning",
"Bread"
],
"lvl1" => [
null,
"Bread > rolls"
]
];
$result = array_filter_recursive($categories);
print '<pre>';
print_r($result);
print '</pre>';
Output:
(
[lvl0] => Array
(
[0] => Cleaning
[1] => Bread
)
[lvl1] => Array
(
[1] => Bread > rolls
)
)
Ref: http://php.net/manual/en/function.array-filter.php#87581
Robbie Averill who commented on my post with the following solved the issue:
$categories['lvl1'] = array_filter($categories['lvl1']);

Categories