Average each associative pair found in a 2d array - php

Consider this collection below:
$collection = [
[1 => 10.0, 2 => 20.0, 3 => 50.0, 4 => 80.0, 5 => 100.0],
[3 => 20.0, 5 => 20.0, 6 => 100.0, 7 => 10.0],
[1 => 30.0, 3 => 30.0, 5 => 10.0, 8 => 10.0]
];
Consider this theorical output based on the intersection of the Arrays contained into $collection, considering their array keys with respective values based on the average of the single values:
$output = Array ( 3 => 33.3333, 5 => 43.3333 );
Can this problem be resolved with a native PHP function like array_intersect_* in an elegant way?
If not, can you suggest me an elegant solution that doesn't necessarily need an outer ugly foreach?
Keep in mind that the number of arrays that need to be intersected is not fixed. It can be 2 input arrays as it can be 1000 input arrays.
Keys will be integers at all times, and Values will be floats or integers at all times.
In other words:
$collection = [
$arr1 = [ ... ];
$arr2 = [ ... ];
$arr3 = [ ... ];
...
$arrn = [ ... ];
];
$output = [ intersected and weighted array based (on comparison) on keys from $arr1 to $arrn, and (on values) from the value averages ];

Count the input array once.
$n = count($collection);
Compute the intersection of all the sub-arrays by key.
$intersection = array_intersect_key(...$collection);
// PHP5: $intersection = call_user_func_array('array_intersect_key', $input);
Build your result by averaging the column from the input array for each key from the intersection.
$output = [];
foreach ($intersection as $key => $value) {
$output[$key] = array_sum(array_column($collection, $key)) / $n;
}
If you really want to completely avoid foreach you can use array_map instead.
$output = array_map(function($key) use ($collection, $n) {
return array_sum(array_column($collection, $key)) / $n;
}, array_keys($intersection));
But in my opinion, this just adds unnecessary complexity.
Note: The values in $intersection will be single values from the first sub-array, but they don't really matter; they're disregarded when generating the output. If it bothers you to have a useless $value variable in the foreach, then you can do foreach (array_keys($intersection) as $key) instead, but I opted for avoiding an unnecessary function call.

Can this problem be resolved with a native PHP function like array_intersect_* in an elegant way?
Well, elegance is in the eye of the developer. If functional-style programming with no new globally-scoped variables equals elegance, then I have something tasty for you. Can a native array_intersect_*() call be leveraged in this task? You bet!
There's a big lack in PHP native functions on intersects - #Maurizio
I disagree. PHP has a broad suite of powerful, optimized, native array_intersect*() and array_diff*() functions. I believe that too few developers are well-acquainted with them all. I've even build a comprehensive demonstration of the different array_diff*() functions (which can be easily inverted to array_intersect*() for educational purposes).
Now, onto your task. First, the code, then the explanation.
Code: (Demo)
var_export(
array_reduce(
array_keys(
array_intersect_ukey(
...array_merge($collection, [fn($a, $b) => $a <=> $b])
)
),
fn($result, $k) => $result + [$k => array_sum(array_column($collection, $k)) / count($collection)],
[]
)
);
The first subtask is to isolate the keys which are present in every row. array_intersect_ukey() is very likely the best qualified tool. The easy part is the custom function -- just write the two parameters with the spaceship in between. The hard part is setting up the variable number of leading input parameters followed by the closure. For this, temporarily merge the closure as an array element onto the collection variable, then spread the parameters into the the native function.
The payload produced by #1 is an array consisting of the associative elements from the first row where the keys were represented in all rows ([3 => 50.0, 5 => 100.0]). To prepare the data for the next step, the keys must be converted to values -- array_keys() is ideal because the float value are of no further use.
Although there is an equal number of elements going into and returning in the final "averaging step", the final result must be a flat associative array -- so array_map() will not suffice. Instead, array_reduce() is better suited. With the collection variable accessible thanks to PHP7.4's arrow function syntax, array_column() can isolate the full column of data then the averaging result pushed as an associative element into the result array.

I guess it could be done like this:
<?php
$intersecting_arrays = Array (
0 => Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
1 => Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
2 => Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 )
);
$temp = $intersecting_arrays[0];
for($i = 1; $i < count($intersecting_arrays); $i++) {
$temp = array_intersect_key($temp, $intersecting_arrays[$i]);
}
$result = Array();
foreach(array_keys($temp) as $key => $val) {
$value = 0;
foreach($intersecting_arrays as $val1) {
$value+= $val1[$val];
}
$result[$key] = $value / count($intersecting_arrays);
}
print_r($temp);
print_r($result);
https://3v4l.org/j8o75
In this manner it doesn't depend on how much arrays you have.
Here you get the intersection of keys in all arrays and then count an average using collected keys.

Ok, with an unknown number of input arrays, I would definitively go with two nested foreach loops to combine them first - getting an unknown number into array_merge_recursive or similar is going to be difficult.
$input = [
0 => [ 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100],
1 => [ 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10],
2 => [ 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10]
];
$combined = [];
foreach($input as $array) {
foreach($array as $key => $value) {
$combined[$key][] = $value;
}
}
$averages = array_map(function($item) {
return array_sum($item)/count($item);
}, $combined);
var_dump($averages);
https://3v4l.org/hmtj5
Note that this solution doesn't need to check for array vs single integer in the array_map callback, because unlike array_merge_recursive, $combined[$key][] inside the loops sees to it that even the keys with just one value will have that value in an array.
EDIT:
but keep in mind that not all the keys are going to be taken into account
Ah, ok, so you want averages only for those keys that occurred more than once. That can easily be fixed by filtering the combined array before using array_map on it:
$combined = array_filter($combined, function($v, $k) {
return count($v) != 1;
}, ARRAY_FILTER_USE_BOTH );
Integrated into above solution: https://3v4l.org/dn5ro
EDIT #2
[Andreas' comment] I think "one" should not be in output since it is not in all three arrays.
Ah, I see ... couldn't tell that was the actually desired result even from the example :-) Then my filtering has to be modified a little bit again, and take the number of input arrays into account:
$combined = array_filter($combined, function($v, $k) use($input) {
return count($v) == count($input);
}, ARRAY_FILTER_USE_BOTH );
https://3v4l.org/9H086

You can merge the arrays to one and use array_sum and count() to get the average.
$arr1 = Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 );
$arr2 = Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 );
$arr3 = Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 );
$array = array_merge_recursive($arr1,$arr2,$arr3);
$key= "two";
If(is_array($array[$key])){
$avg = array_sum($array[$key])/count($array[$key]);
}Else{
$avg = $array[$key];
}
Echo $avg;
https://3v4l.org/pa3PH
Edit to follow $collection array.
Try this then. Use array column to grab the correct key and use array_sum and count to get the average.
$collection = array(
Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));
$key= "three";
$array = array_column($collection, $key);
If(count($array) != 1){
$avg = array_sum($array)/count($array);
}Else{
$avg = $array[0];
}
Echo $avg;
https://3v4l.org/QPsiS
Final edit.
Here I loop through the first subarray and use array column to find all the matching keys.
If the count of keys is the same as the count of collection the key exsists in all subarrays and should be "saved".
$collection = array(
Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));
Foreach($collection[0] as $key => $val){
$array = array_column($collection, $key);
If(count($array) == count($collection)){
$avg[$key] = array_sum($array)/count($array);
}
}
Var_dump($avg);
https://3v4l.org/LfktH

Related

how to fix array_udiff while having strings inside array [duplicate]

I have an array containing rows of associative data.
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
I have a second array, also containing rows of associative data, that I would like to filter using the first array.
$array2 = array(
array('ITEM' => 2),
array('ITEM' => 3),
array('ITEM' => 1),
array('ITEM' => 4),
);
This feels like a job for array_diff(), but how can I compare the rows exclusively on the deeper ITEM values?
How can I filter the second array and get the following result?
array(3 => array('ITEM' => 4))
You can define a custom comparison function using array_udiff().
function udiffCompare($a, $b)
{
return $a['ITEM'] - $b['ITEM'];
}
$arrdiff = array_udiff($arr2, $arr1, 'udiffCompare');
print_r($arrdiff);
Output:
Array
(
[3] => Array
(
[ITEM] => 4
)
)
This uses and preserves the arrays' existing structure, which I assume you want.
I would probably iterate through the original arrays and make them 1-dimensional... something like
foreach($array1 as $aV){
$aTmp1[] = $aV['ITEM'];
}
foreach($array2 as $aV){
$aTmp2[] = $aV['ITEM'];
}
$new_array = array_diff($aTmp1,$aTmp2);
Another fun approach with a json_encode trick (can be usefull if you need to "raw" compare some complex values in the first level array) :
// Compare all values by a json_encode
$diff = array_diff(array_map('json_encode', $array1), array_map('json_encode', $array2));
// Json decode the result
$diff = array_map('json_decode', $diff);
A couple of solutions using array_filter that are less performant than the array_udiff solution for large arrays, but which are a little more straightforward and more flexible:
$array1 = [
['ITEM' => 1],
['ITEM' => 2],
['ITEM' => 3],
];
$array2 = [
['ITEM' => 2],
['ITEM' => 3],
['ITEM' => 1],
['ITEM' => 4],
];
$arrayDiff = array_filter($array2, function ($element) use ($array1) {
return !in_array($element, $array1);
});
// OR
$arrayDiff = array_filter($array2, function ($array2Element) use ($array1) {
foreach ($array1 as $array1Element) {
if ($array1Element['ITEM'] == $array2Element['ITEM']) {
return false;
}
}
return true;
});
As always with array_filter, note that array_filter preserves the keys of the original array, so if you want $arrayDiff to be zero-indexed, do $arrayDiff = array_values($arrayDiff); after the array_filter call.
you can use below code to get difference
$a1 = Array(
[0] => Array(
[ITEM] => 1
)
[1] => Array(
[ITEM] => 2
)
[2] => Array(
[ITEM] => 3
)
);
$a2 = Array(
[0] => Array(
[ITEM] => 2
)
[1] => Array(
[ITEM] => 3
)
[2] => Array(
[ITEM] => 1
)
[3] => Array(
[ITEM] => 4
));
array_diff(array_column($a1, 'ITEM'), array_column($a2, 'ITEM'));
Having the same problem but my multidimensional array has various keys unlike your "ITEM" consistently in every array.
Solved it with: $result = array_diff_assoc($array2, $array1);
Reference: PHP: array_diff_assoc
Another solution
if( json_encode($array1) == json_encode($array2) ){
...
}
Trust that the maintainers of PHP have optimized array_udiff() to outperform all other techniques which could do the same.
With respect to your scenario, you are seeking a filtering array_diff() that evaluates data within the first level's "value" (the row of data). Within the custom function, the specific column must be isolated for comparison. For a list of all native array_diff() function variations, see this answer.
To use the first array to filter the second array (and output the retained data from the second array), you must write $array2 as the first parameter and $array1 as the second parameter.
array_diff() and array_intersect() functions that leverage (contain u in their function name) expect an integer as their return value. That value is used to preliminary sort the data before actually performing the evaluations -- this is a performance optimization. There may be scenarios where if you only return 0 or 1 (not a three-way comparison), then the results may be unexpected. To ensure a stable result, always provide a comparison function that can return a negative, a positive, and a zero integer.
When comparing integer values, subtraction ($a - $b) will give reliable return values. For greater utility when comparing float values or non-numeric data, you can use the spaceship operator when your PHP version makes it available.
Codes: (Demo)
PHP7.4+ (arrow functions)
var_export(
array_udiff($array2, $array1, fn($a, $b) => $a['ITEM'] <=> $b['ITEM'])
);
PHP7+ (spaceship operator)
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] <=> $b['ITEM'];
}
)
);
PHP5.3+ (anonymous functions)
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] === $b['ITEM']
? 0
: ($a['ITEM'] > $b['ITEM'] ? 1 : -1);
}
)
);
Output for all version above:
array (
3 =>
array (
'ITEM' => 4,
),
)
When working with object arrays, the technique is the same; only the syntax to access a property is different from accessing an array element ($a['ITEM'] would be $a->ITEM).
For scenarios where the element being isolated from one array does not exist in the other array, you will need to coalesce both $a and $b data to the opposite fallback column because the data from the first array and the second arrays will be represented in both arguments of the callback.
Code: (Demo)
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
$array2 = array(
array('ITEMID' => 2),
array('ITEMID' => 3),
array('ITEMID' => 1),
array('ITEMID' => 4),
);
// PHP7.4+ (arrow functions)
var_export(
array_udiff(
$array2,
$array1,
fn($a, $b) => ($a['ITEM'] ?? $a['ITEMID']) <=> ($b['ITEM'] ?? $b['ITEMID'])
)
);
Compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays.
//Enter your code here, enjoy!
$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_diff($array1, $array2);
print_r($result);

Merge arrays on one column, sum other column values in each group [duplicate]

This question already has answers here:
Group 2d array data by one column and sum other columns in each group (separately)
(4 answers)
Closed last month.
I have 2 arrays:
$array1 = [
['amount' => 21600.00, 'rows' => 2, 'student_id' => 1],
];
$array2 = [
['amount' => 541990.00, 'rows' => 512, 'student_id' => 1],
['amount' => 347480.00, 'rows' => 281, 'student_id' => 2],
['amount' => 507400.00, 'rows' => 214, 'student_id' => 3],
];
I want to merge both arrays based on the same student_id value. When a student_id is found in both arrays, I want to sum the two amount values and the two rows values.
Expected result:
array (
0 =>
array (
'amount' => 563590.0,
'rows' => 514,
'student_id' => 1,
),
1 =>
array (
'amount' => 347480.0,
'rows' => 281,
'student_id' => 2,
),
2 =>
array (
'amount' => 507400.0,
'rows' => 214,
'student_id' => 3,
),
)
I have tried using array_merge() and array_merge_recursive() but they didn't give me the expected result.
Here is simple code to solve your problem,
count($arr) > count($arr0) ? ($greater = $arr AND $smaller = $arr0) : ($smaller = $arr AND $greater = $arr0);
foreach ($greater as $key => &$value) {
foreach ($smaller as $key1 => &$value1) {
if($value['student_id'] == $value1['student_id']){
$value['amount'] +=$value1['amount'];
$value['rows'] +=$value1['rows'];
}
}
}
Check output here
The comment about combining the queries is probably the better approach.
SELECT student_id, sum(amount) as amount, sum(rows) as rows
from students
group by student_id;
However, if you can't do it via a query, PHP doesn't natively add array values together (it doesn't make sense)
Here's what you'd have to do
function arrayMergeMatch(&$a, &$b)
{
foreach ($a as $key => $value) {
if (array_key_exists($key, $b)) {
// there's a matching index in both a & b
$a[$key] = [
$a[$key]['amount'] += $b[$key]['amount'],
$a[$key]['rows'] += $b[$key]['rows'],
$a[$key]['student_id'],
];
}
}
}
$firstArray = $secondArray = [];
$firstArray[0] = [
'amount' => 21600.00,
'rows' => 2,
'student_id' => 1
];
$secondArray[0] = [
'amount' => 541990.00,
'rows' => 512,
'student_id' => 1,
];
$secondArray[1] = [
'amount' => 347480.00,
'rows' => 281,
'student_id' => 2,
];
$secondArray[2] = [
'amount' => 507400.00,
'rows' => 214,
'student_id' => 3,
];
$firstArrayLength = count($x);
$secondArrayLength = count($y);
$combinedArray = null;
if ($firstArrayLength > $secondArrayLength) {
// loop through x checking to see if there's a matching y
arrayMergeMatch($firstArray, $secondArray);
$combinedArray = $firstArray;
}
else {
// y is longer than or equal to x, so loop through y, looking for matching
// index in x
arrayMergeMatch($secondArray, $firstArray);
$combinedArray = $secondArray;
}
print_r($combinedArray);
Nested loops are not necessary. There will be several different styles, but the crux of the task is to create a result array that temporarily serves as a lookup array while you iterate the incoming data.
To do this, assign temporary keys using student_id values. When a student_id is encountered after it is found in the result array, just add the values to the stored related values in the group. When finished iterating, re-index the result with array_values().
Functional style: (Demo1) (Demo2 - likely slower)
var_export(
array_values(
array_reduce(
$array2,
function($result, $row) {
if (!isset($result[$row['student_id']])) {
$result[$row['student_id']] = $row;
} else {
$result[$row['student_id']]['amount'] += $row['amount'];
$result[$row['student_id']]['rows'] += $row['rows'];
}
return $result;
},
array_column($array1, null, 'student_id')
)
)
);
Language construct loop: (Demo1) (Demo2 - likely slower)
$result = array_column($array1, null, 'student_id');
foreach ($array2 as $row) {
if (!isset($result[$row['student_id']])) {
$result[$row['student_id']] = $row;
} else {
$result[$row['student_id']]['amount'] += $row['amount'];
$result[$row['student_id']]['rows'] += $row['rows'];
}
}
var_export(array_values($result));
All of the above snippets should be used instead of the other posted answers because they will never iterate more than m + n. Here's a critique of the other answers:
Rahul's answer is using a nested loop -- this means the number of iterations will be m*n (number of rows in array1 multiplied by number of rows in array2) -- this is very indirect/inefficient. Even if a break was used, it still wouldn't be as direct/efficient as my snippets.
Kearney's answer is completely ignoring the requirement to sort on student_id values (even after the $x and $y typos are fixed). This php-based answer is simply incorrect. Proof. Less importantly, $b does not need to be made modifiable by reference.

How to replace key in multidimensional array and maintain order

Given this array:
$list = array(
'one' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
'two' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
'three' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
'four' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
),
'five' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
);
I need a function(replaceKey($array, $oldKey, $newKey)) to replace any key 'one', 'two', 'three', 'four' or 'five' with a new key independently of the depth of that key. I need the function to return a new array, with the same order and structure.
I already tried working with answers from this questions but I can't find a way to keep the order and access the second level in the array:
Changing keys using array_map on multidimensional arrays using PHP
Change array key without changing order
PHP rename array keys in multidimensional array
This is my attempt that doesn't work:
function replaceKey($array, $newKey, $oldKey){
foreach ($array as $key => $value){
if (is_array($value))
$array[$key] = replaceKey($value,$newKey,$oldKey);
else {
$array[$oldKey] = $array[$newKey];
}
}
return $array;
}
Regards
This function should replace all instances of $oldKey with $newKey.
function replaceKey($subject, $newKey, $oldKey) {
// if the value is not an array, then you have reached the deepest
// point of the branch, so return the value
if (!is_array($subject)) return $subject;
$newArray = array(); // empty array to hold copy of subject
foreach ($subject as $key => $value) {
// replace the key with the new key only if it is the old key
$key = ($key === $oldKey) ? $newKey : $key;
// add the value with the recursive call
$newArray[$key] = replaceKey($value, $newKey, $oldKey);
}
return $newArray;
}

Most efficient way to mathematically add two multidimensional arrays?

Consider two simple arrays:
<?php
$array1 = array(
'blue' => 5,
'green' => array(
'square' => 10,
'sphere' => 0.5,
'triangle' => 3
),
'red' => array(
'circle' => 1000,
),
'black' => 4,
);
$array2 = array(
'blue' => 1,
'green' => array(
'square' => 11,
'circle' => 5,
),
'purple' => 10,
'yellow' => array(
'triangle' => 4
),
'black' => array(
'circle' => 6,
),
);
I need to mathmatically add together in a recursive way, each value from each $array1 and $array2.
Preserve keys
Where a key does not exist in $array1 but does exist in $array2, the final array would simply contain the value of $array2 (and the other way around as well)
Where they exist in both, the numeric values would be added +
Non-numeric values wouldn't be touched
If a value on $array1 points to another sub-array, and in $array2 it points to a value, the end value would result in that key containing a subarray that contains the values from $array1 plus a new key/value using the parent name and it's value (see black in the example)
Should be able to work at virtually unlimited nesting
To clarify, e.g. if we said
<?php
$final = array_merge_special($array1, $array2);
// We would end up with, if you var_export()'d final, something like:
// (Note: Hope I didn't make mistakes in this or it will be confusing,
// so expect mild human error)
$final = array(
'blue' => 6, // 5+1
'green' => array(
'square' => 21, // (10+11)
'sphere' => 0.5, // only in $array1
'triangle' => 3 // only in $array1
'circle' => 5, // only in $array2
),
'purple' => 10, // only in $array2
'yellow' => array( // only in $array2
'triangle' => 4
),
'red' => array( // only in $array1
'circle' => 1000,
),
'black' => array(
'circle' => 6, // untouched, while $black is present in both, the $array1 value does not have a 'circle' key, and is actually only a key/value (see below)
'black' => 4, // the key/value from $array1 that was not a subarray, even though it was a subarray in $array2
),
);
This seems outragously daunting to me. I know I could loop over one array and get easily recursively add the values, and I have this working (somewhat), but it's when I get into special rules (such as ones for black) that I can't even imagine how broken code would look. There has to be a way to do this would looping over each array individually and unset()'ing values to merge?
You would use array_walk_recursive (see: See php Manual here) and possibly array_merge_recursive. I'd have to think it through further to get the full picture.
OK, decided that this wouldn't work! Array_walk_recursive doesn't pass keys that hold arrays to the function. This problem kept flowing aroung in my brain, so I just had to write a function to do it! Here it is:
function dosum($arin) {
$arout = array();
foreach ($arin as $key1 => $item1) {
$total = 0;
if(is_array($item1)) {
foreach($item1 as $key2 => $item2) {
if(is_numeric($key2))
$total += $item2;
else
if(is_array($item2))
$arout[$key1] = dosum(array($key2 => $item2));
else
$arout[$key1][$key2] =$item2;
}
if($total)
if(isset($arout[$key1]))
$arout[$key1][$key1] = $total;
else
$arout[$key1] = $total;
}
else
$arout[$key1] = $item1;
}
return $arout;
}
For the 2 arrays given, you would use it like this:
print_r(dosum(array_merge_recursive($array1, $array2)));

Hard PHP nut : how to insert items into one associate array?

Yes, this is a bit of a trick question; one array (without copies), as opposed to any odd array. Let me explain, so let's start here ;
$a = array ( 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5, 'six' => 6 ) ;
Pretend that this array is long, over a hundred long. I loop through it, step by step, but at some point (let's make up that this happens at the second item) something happens. Maybe the data is funky. Nevertheless, we need to add some items to it for later processing, and then keep looping through it, without losing the current position. Basically, I would like to do something like this ;
echo current ( $a ) ; // 'two'
array_insert ( $a, 'four', 'new_item', 100 ) ;
echo current ( $a ) ; // 'two'
Definition for array_insert is ( $array, $key_where_insert_happens, $new_key, $new_value ) ; Of course $new_key and $new_value should be wrapped in an array wrapper, but that's besides the point right now. Here's what I want to see happening after the above code having ran ;
print_r ( $a ) ; // array ( 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'new_item' => 100, 'five' => 5, 'six' => 6 ) ;
echo current ( $a ) ; // 'two'
Whenever you use array_splice, array_slice, array_push or most of the other array fiddling functions, you basically create a copy of the array, and then you can copy it back, but this breaks the reference to the original array and the position as well, and my loop above breaks. I could use direct reference (ie. $a['new_item'] = 'whatever;) or put it at the end, but none of these will insert items into a given position.
Any takers? How can I do a true insert into an associative array directly (that's being processed elsewhere)? My only solution so far is to ;
record the position (current())
Do the splice/insert (array_slice)
overwrite old array with new ($old = $new)
search the new position (first reset() then looping through to find it [!!!!!!])
Surely there's a better, simpler and more elegant way for doing something that's currently kludgy, heavy and poorly hobbled together? Why isn't there a array_set_position ( $key ) function that quickly can help this out, or an array_insert that works directly on the same array (or both)?
Maybe I'm not understanding you correctly but have you looked into array_splice()?
This answer might also interest you.
Would something like this work?
function array_insert($input, $key, $value)
{
if (($key = array_search($key, array_keys($input))) !== false)
{
return array_splice($input, $key, 1, $value);
}
return $input;
}
This was the best I could come up with:
$a = array
(
'one' => 1,
'two' => 2,
'three' => 3,
'four' => 4,
'five' => 5,
'six' => 6,
);
ph()->Dump(next($a)); // 2
array_insert($a, 'four', array('new_item' => 100));
ph()->Dump(current($a)); // 2
function array_insert(&$array, $key, $data)
{
$k = key($array);
if (array_key_exists($key, $array) === true)
{
$key = array_search($key, array_keys($array)) + 1;
$array = array_slice($array, null, $key, true) + $data + array_slice($array, $key, null, true);
while ($k != key($array))
{
next($array);
}
}
}
ph()->Dump($a);
/*
Array
(
[one] => 1
[two] => 2
[three] => 3
[four] => 4
[new_item] => 100
[five] => 5
[six] => 6
)
*/
I don't think it's possible to set an array internal pointer without looping.
Rather than using the associative array as the event queue, keep a separate integer-indexed array. Loop over an explicit index variable rather than using the internal array position.
$events = array_keys($a);
for ($i=0; $i < count($events); ++$i) {
...
/* if there's an event to add after $j */
array_splice($events, $j+1, 0, array($new_event_key));
$a[$new_event_key] = $new_event_data;
...
}
To keep things more cohesive, you can package the two arrays into an event queue class.

Categories