I have already asked this question once for a solution in Python:
python intersect of dict items
but I am now looking for the equivalent solution in PHP. Specifically I am using CakePHP 2.0 on the ConsoleShell.
I have an undetermined number of nests in an array and I want to loop over them like this:
$array = array(1 => array(1, 2, 3, 4), 2 => array(7, 8), n => array(n1, n2, n3))
foreach($array[1] as $k1 => $v1) {
foreach($array[2] as $k2 => $v2) {
foreach($array[n] as $kn => $vn) {
// do something with this combination
}
}
}
Bear in mind that n can be any number (the total number of nests), so I need code that allows for a dynamic number of nests.
Any ideas please?
Thanks
EDIT:
Just to clarify what I am after, if
$array = array(array(0, 1, 2, 3), array(0, 6, 7), array(0, 9, 10));
I want to end up with:
$array2 = array(
array(0, 0, 0),
array(0, 0, 9),
array(0, 0, 10),
array(0, 6, 0),
array(0, 6, 9),
...
array(3, 6, 10),
array(3, 7, 0),
array(3, 7, 9),
array(3, 7, 10)
);
however if $array has 4 array elements, then $array2 should have 4 elements on each line etc.
function dosomething($a) {
if (is_array($a))
foreach ($a as $aa)
dosomething($aa)
else {
//do something
}
}
Edit:
After the OQ was updated it became clear, that not the depth is variable, but the element count. So we do not need recursion, but nested foreaches
This gives the output as requested in the OQ
<?php
$array = array(array(0, 1, 2, 3), array(0, 6, 7), array(0, 9, 10));
$result=array(array());
foreach ($array as $a) {
$oldresult=$result;
$result=array();
foreach ($oldresult as $or) {
foreach($a as $aa) {
$nr=$or;
$nr[]=$aa;
$result[]=$nr;
}
}
}
print_r($result);
?>
In short: We start with a result array, that has one element: an empty array. Now we cycle through the first level of the input array, in every step we replace all elements of the result array with a series of elements consiting of the original element with each of the members of our current array attached.
The first iteration replaces
array() with array(0), array(1), array(2), array(3)
The second iteration replaces
array(0) with array(0,0), array(0,6), array(0,7)
array(1) with array(1,0), array(1,6), array(1,7)
...
The third iteration replaces
array(0,0) with array(0,0,0), array(0,0,9), array(0,0,10)
array(0,1) with array(0,1,0), array(0,1,9), array(0,1,10)
...
array(1,0) with array(1,0,0), array(1,0,9), array(1,0,10)
...
Related
$array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
I want output like
['1, 2, 3, 4', '5, 6, 7, 8', '9'];
You can use array_chunk() to split your array into chunks of 4 items. Then array_map() to implode() each chunk:
$array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
$chunk = array_chunk($array, 4);
var_dump(array_map(fn($item) => implode(', ', $item), $chunk));
Outputs:
array(3) {
[0]=> string(7) "1, 2, 3, 4"
[1]=> string(7) "5, 6, 7, 8"
[2]=> string(1) "9"
}
you can do that
const $array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const $chunk = $array.reduce((r,v,i)=>
{
if (!(i%4)) r.pos = r.resp.push(`${v}`) -1
else r.resp[r.pos] += `,${v}`
return r
}, { resp : [], pos:0 } ).resp
console.log( $chunk )
For best efficiency in terms of time complexity, loop over the array only once. As you iterate, divide the index by 4 and "floor" or "truncate" the dividend to form grouping keys.
When a grouping key is encountered more than once, prepend the delimiting comma-space before appending the new string value to the group's string.
Code: (Demo)
$array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
foreach ($array as $i => $v) {
$group = (int) ($i / 4);
if (isset($result[$group])) {
$result[$group] .= ", $v";
} else {
$result[$group] = "$v";
}
}
var_export($result);
More concisely, you can split the array into 4-element rows and then implode those elements to form your delimited strings. This has more expensive time complexity because it iterates more times than the number of elements in the input array. (Demo)
var_export(
array_map(
fn($v) => implode(', ', $v),
array_chunk($array, 4)
)
);
You could also consume the input array as you isolate 4 elements at a time with array_splice(), but this is probably the least advisable since it eventually erases all of the data in the input array. (Demo)
$result = [];
while ($array) {
$result[] = implode(', ', array_splice($array, 0, 4));
}
var_export($result);
Output from all above techniques:
array (
0 => '1, 2, 3, 4',
1 => '5, 6, 7, 8',
2 => '9',
)
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have two arrays. For example:
$arr1 = [1, 2, 3, 7, 8, 9];
$arr2 = [4, 5, 6, 10, 11, 12, 13, 14];
How can I combine them to general array by taking per 3 elements from every array? The output should be:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
The main idea is taking per 3 elements. Later when I understand the algorithm, the arrays will have objects, not digits. I need to understand how I can to combine two arrays by taking per 3 elements from every array.
This came to mind...
Just keep looping while either array has any elements.
Suck the first three elements from each array and push them into your result array until there is nothing left in the two arrays.
Seems simple enough to me. :)
Code: (Demo)
$arr1 = [1, 2, 3, 7, 8, 9];
$arr2 = [4, 5, 6, 10, 11, 12, 13, 14];
$result = [];
while ($arr1 || $arr2) {
array_push($result, ...array_splice($arr1, 0, 3), ...array_splice($arr2, 0, 3));
}
var_export($result);
Output:
array (
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 5,
5 => 6,
6 => 7,
7 => 8,
8 => 9,
9 => 10,
10 => 11,
11 => 12,
12 => 13,
13 => 14,
)
If you don't want to mutate your original input arrays, this will do: (Demo)
$result = [];
for ($i = 0; isset($arr1[$i]) || isset($arr2[$i]); $i += 3) {
array_push($result, ...array_slice($arr1, $i, 3), ...array_slice($arr2, $i, 3));
}
var_export($result);
So like the comment said you can just merge then sort. The reason for this and why it is a recommended approach is due to the fact that the sort and merge operations apply on the highest level key-value and do not affect sub-objects/arrays inside the merged array.
<?PHP
$array1 = array("color" => "red", 2, 4);
$array2 = array("a", "b", "color" => "green", "shape" => "trapezoid", 4);
$result = array_merge($array1, $array2);
sort($result);
print_r($result);
?>
However, if you really wanted to do an "every 3 elements" method. You can do it by
looping from 0 to n_smallest_length array and then just appending the rest of the larger array to the end and then sorting in 2 places to make sure the next set is being appended into an already sorted array
sorting the array at each iteration
or sorting at the end
<?PHP
$array_one_size = sizeof($array1);
$array_two_size = sizeof($array2);
$smallest_size = $array_one_size < $array_two_size ? $array_one_size : $array_two_size;
$result = array();
for ($i = 0; $i < $smallest_size; $i += 3) {
$result_tmp = array($array1[$i], $array1[$i + 1], $array1[$i + 2], $array2[$i], $array2[$i + 1], $array2[$i + 2]);
$result = array_merge($result, $result_tmp);
sort($result);
}
if ($smallest_size == $array_one_size) {
$result = array_merge($result, array_slice($array1, $i);
} else {
$result = array_merge($result, array_slice($array2, $i);
}
sort($result);
print_r($result);
?>
Note: Also, the case shown above works really well with easily sortable elements. If you want to go for a more complex element array then just replace the sort($result); lines with a custom sorting algorithm that fits your needs.
Given two arrays $A1 and $A2, sort $A1 in such a way that the relative order among the elements will be same as those in $A2. For the elements not present in $A2, move them to the back of the array in ascending order.
$A1 = [2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8];
$A2 = [2, 1, 8, 3];
Desired output:
[2, 2, 1, 1, 8, 8, 3, 5, 6, 7, 9]
Coding attempt:
$sorted = array();
foreach($a1 as $key => $value) {
if(in_array($value, $a2)) {
$sorted[array_search($value, $a1)] = $value;
}
}
This can be done via for each loop :
$arr1 = array(2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8); // array to be sorted
$arr2 = array(2, 1, 8, 3); // refrence array for sort logic
// Output: A1[] = {2, 2, 1, 1, 8, 8, 3, 5, 6, 7, 9}
$sortarr = array(); // array to store final sorted values
foreach ($arr2 as $a) {
foreach ($arr1 as $k => $b) {
if($b==$a) {
$sortarr[]=$b;
unset($arr1[$k]);
}
}
}
$finalarr = array_merge($sortarr, $arr1);
print_r($finalarr);
You can use usort like this:
$k = array_flip($a2); // Create an associative array for the second array
usort($a1, function($a, $b) use ($k) {
return isset($k[$a]) ? (isset($k[$b]) ? $k[$a]-$k[$b] : -1) : (isset($k[$b]) ? 1 : $a-$b);
});
Other solutions that use a nested loop result in a time complexity of O(n²), while this has a time complexity of O(nlogn).
i tried this code and works:
<?php
/*
Input: A1[] = {2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8}
A2[] = {2, 1, 8, 3}
Output: A1[] = {2, 2, 1, 1, 8, 8, 3, 5, 6, 7, 9}
*/
$a1=array(2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8);
$a2=array(2, 1, 8, 3);
$a3=array();
sort($a1);//order array
//order array a3 with a2 value....
for($i=0;$i<sizeof($a2);$i++){
for($j=0;$j<sizeof($a1);$j++){
if($a1[$j]==$a2[$i]){
array_push($a3,$a2[$i]);
//if exsist value i change value in a1 in x
$a1[$j]="x";
}
}
}
//write in a3 the next number not present in a2
for($i=0;$i<sizeof($a1);$i++){
if($a1[$i]<>"x"){
array_push($a3, $a1[$i]);
}
}
print_r($a3);
//out:Array ( [0] => 2 [1] => 2 [2] => 1 [3] => 1 [4] => 8 [5] => 8 [6] => 3 [7] => 5 [8] => 6 [9] => 7 [10] => 9 )
?>
Hope this helps
I do not endorse the use of nested loops because they will be doing too many unnecessary cycles. #trincot's approach moreso represents what I would use in a professional application, but I will demonstrate a more modern and concise style with no iterated function calls.
Code: (Demo)
$arr1 = [2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8];
$arr2 = [2, 1, 8, 3];
$priority = array_flip($arr2);
$fallback = count($arr2);
usort(
$arr1,
fn($a, $b) =>
[$priority[$a] ?? $fallback, $a]
<=>
[$priority[$b] ?? $fallback, $b]
);
var_export($arr1);
// [2, 2, 1, 1, 8, 8, 3, 5, 6, 7, 9]
$arr2 is flipped into a lookup array, and count() is used when a value is not found in the lookup array. Whenever the first rule results in a tie, the second rule orders the numbers numerically in an ascending direction.
The meaning of custom function's syntax is:
[left value rule1, left value rule2] compared to [right value rule1, right value rule2]
The spaceship operator (<=>) will compare the corresponding element one at a time "rule1 vs rule1" then "rule2 vs rule2" as needed.
$myArray = array(2, 7, 4, 2, 5, 7, 6, 7);
$uniques = array_unique($myArray);
Along with displaying each value in the array only once, how would I ALSO display (in the foreach loop below) the number of times each value is populated in the array. IE next to '7' (the array value), I need to display '3' (the number of times 7 is in the array)
foreach ($uniques as $values) {
echo $values . " " /* need to display number of instances of this value right here */ ;
}
Use the array_count_values function instead.
$myArray = array(2, 7, 4, 2, 5, 7, 6, 7);
$values = array_count_values($myArray);
foreach($values as $value => $count){
echo "$value ($count)<br/>";
}
Have a look at array_count_values.
Quoting from the manual:
Example #1 array_count_values() example
<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>
The above example will output:
Array
(
[1] => 2
[hello] => 2
[world] => 1
)
Hay i have an array consisting for 6 random numbers, here's an example
[4,8,12,22,23,43]
I also have 100 arrays containing 6 numbers, these are all random a few examples could be
[5,8,15,47,32,48]
[3,4,8,12,33,42]
[8,12,26,55,43,33]
[4,63,45,23,45,55] ...
I want to see how many times (out of the array of 100) these numbers match at least 3 from the top array. As you can guess this is a lottery experiment.
As you can see array number 3 matches 3 numbers from the top array.
Any ideas how do do this? With perhaps an option to see if 4 numbers matched.
$master_array = array(4, 8, 12, 22, 23, 43);
$arrays = array(array(5, 8, 15, 47, 32, 48),
array(3, 4, 8, 12, 33, 42),
array(8, 12, 26, 55, 43, 33),
array(4, 63, 45, 23, 45, 55));
foreach ($arrays as $arr)
{
$intersect = array_intersect($master_array, $arr);
if (count($intersect)==3) print 'Match: '.print_r($arr, true).PHP_EOL;
}
maybe smth like this:
$winner = [4,8,12,22,23,43];
$arrays = //all your 100 arrays
$i = 0; // number of matches
foreach ($arrays as $array)
{
$result = array_intersect($array, $winner);
if (count($result) >= 3) $i++;
}