PHP: Nested loop with only unique pairs - php

I need to create an array of only unique pairs so there are no duplicates.
The problem is that when just iterating over each other, for some reason last pair usually is duplicate.
So here's a quick example:
<?php
$teams = [
['id' => 1],
['id' => 2],
['id' => 3],
['id' => 4],
];
foreach ($teams as $team_a) {
foreach ($teams as $team_b) {
if ($team_a['id'] !== $team_b['id']) {
$pairs[] = [$team_a['id'], $team_b['id']];
}
}
}
?>
Returns something like this:
0: [1, 2]
1: [1, 3]
2: [1, 4]
3: [2, 1]
4: [2, 3]
5: [2, 4]
6: [3, 1]
7: [3, 2]
8: [3, 4]
...
So you can see that some pairs are the same, like [1, 2] and [2, 1]. And after the half iterations there are only repeats.
What would be the most efficient way to iterate like this and be sure that there are only unique pairs?
Thanks!

Obviously the duplicate always appears when key(team_a) > key(team_b)
<?php
$teams = [
['id' => 1],
['id' => 2],
['id' => 3],
['id' => 4],
];
foreach ($teams as $offset => $team_a) {
foreach (array_slice($teams, $offset+1) as $team_b) {
$pairs[] = [$team_a['id'], $team_b['id']];
}
}

You could to do this with recursion instead of two for each loops.
<?php
$teams = [1, 2, 3, 4, 5, 6, 7, 8, 9 , 10];
getMatchup($teams);
function getMatchUp($teams, $matches = [], $start = 0) {
// Check if the whole array has been checked
if ($start == count($teams)) {
return var_dump($matches);
}
// Check every option considering a certain start point
for ($x = $start; $x < count($teams); $x++) {
// As long the team is not the same, add to matches
if ($start !== $x) {
$matches[] = [$teams[$start] => $teams[$x]];
}
}
// First team has been matched up, start matching the second team and so on..
getMatchup($teams,$matches, $start + 1);
}
Running example:
http://sandbox.onlinephpfunctions.com/code/4277d966c4ceacdbe7024d14fb03fe05fd760471

Related

How to Create matrix of dynamic arrays into one array?

I have dynamic number of arrays like:
$a= [ 1, 2 ];
$b= [ 3, 4, 5 ];
$c= [ 6 ];
I want to merge every number from each row with parent values sustaining till all child values complete. So starting from lowest level, the matrix gets created as follows:
Third level:
[ 6 ]
Second level:
[
[3 , 6] ,
[4 , 6] ,
[5 , 6]
]
First/Top level(final level):
[
[1, 3, 6] ,
[1, 4 , 6] ,
[1, 5 , 6] ,
[2, 3, 6] ,
[2, 4 , 6] ,
[2, 5 , 6]
]
Note: The number of arrays can vary and the number of elements in each array might also change.
I want a way to get those into matrix like above in the final result. Any ideas on how to get started?
I've had the same problem before , here what tried.
1-You can use crossJoin helper with collection
$results=collect($a)->crossJoin($b,$c);
2-You can use some thing native like this
function combinations($arrays, $i = 0) {
if (!isset($arrays[$i])) {
return array();
}
if ($i == count($arrays) - 1) {
return $arrays[$i];
}
// get combinations from subsequent arrays
$tmp = combinations($arrays, $i + 1);
$result = array();
// concat each array from tmp with each element from $arrays[$i]
foreach ($arrays[$i] as $v) {
foreach ($tmp as $t) {
$result[] = is_array($t) ?
array_merge(array($v), $t) :
array($v, $t);
}
}
return $result;
}
dd(
combinations(
array(
array('1','2','3'),
array('4','5','6'),
array('7','8')
)
)
);

Finding linking path between two points in array php

I would like to find possible options from an array that connects two points.
Eg.
$startPoint = 1;
$endPoint = 5;
$points = [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 5] [4, 3], [1, 4], [1, 3], [1, 5], [1, 6]];
function should return
$possibleOptions = [[1, 5], [[1, 4], [4, 5]], [[1, 6], [6, 5]], [[1, 2], [2, 3], [3, 4], [4, 5]]]
Would be nice to add a possible option limit eg. if maximum of 3 combinations are allowed, then the function would return
$possibleOptions = [[1, 5], [[1, 4], [4, 5], [[1, 6], [6, 5]]
as [[[1, 2], [2, 3], [3, 4], [4, 5]]] is a total of 4 combinations.
I have tried looping through the array and finding a direct match first. If no direct match is found i loop through points again and create a new array with all the points that have start point as first value. I then check if the new array has an element where the last value is the endpoint. However, i feel like it is repetitive code and could be optimized as I have to repeat the code for the the max amount of combinations. Just not sure how...
Here is an example of my code:
$results = [];
$startPoint = 1;
$endPoint = 3;
$points = [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [4, 3], [1, 4], [1, 3]];
foreach ($points as $point) {
if ($point[0] == $startPoint && $point[1] == $endPoint) {
$results[] = $point;
}
}
$newStartPoints = [];
foreach ($points as $point) {
if ($point[0] == $startPoint) {
$newStartPoints[] = $point;
}
}
foreach ($points as $point) {
foreach ($newStartPoints as $startPoint) {
if ($point[0] == $startPoint[1] && $point[1] == $endPoint) {
$results[] = [$startPoint, $point];
}
}
}
print_r($results);
Here is a solution:
$startPoint = 1;
$endPoint = 5;
$max_combinations = 3;
$points = [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [4, 3], [1, 4], [1, 3], [1, 5]];
$result = [];
for ($i = $startPoint; $i <= $endPoint; $i++) {
$sub_result = [];
if (in_array([$startPoint, $i], $points)) {
$sub_result[] = [$startPoint, $i];
for ($j = $i; $j <= $endPoint; $j++) {
if (in_array([$j, $j + 1], $points) && $j + 1 <= $endPoint) {
$sub_result[] = [$j, $j + 1];
}
}
}
if (count($sub_result) != 0 && (count($sub_result) <= $max_combinations || $max_combinations === -1)) {
$result[] = $sub_result;
}
}
sort($result);
print_r($result);
The first four variable declarations can be modified according to your needs.
Note that setting $max_combinations equal to -1 it will mean that it will match all paths rather than having a limit.
Live demo

Group Array By Range Value

I have this array [1,1,2,2,2,3,4,4,5,6,6,6,7], may I group the array according to the range value, so get the final result:
'1-3' = [1,1,2,2,2,3]; // Count is 6
'4-5' = [4,4,5]; // Count is 3
'6-7' = [6,6,6,7]; // Count is 4
What you need I believe is:
function array_get_range($array, $min, $max) {
return array_filter($array, function($element) use ($min, $max) {
return $element >= $min && $element <= $max;
});
}
$array = [1,1,2,2,2,3,4,4,5,6,6,6,7];
$range13 = array_get_range($array, 1, 3); // [1, 1, 2, 2, 2, 3]
$range45 = array_get_range($array, 4, 5); // [4, 4, 5]
$range67 = array_get_range($array, 6, 7); // [6, 6, 6, 7]
Create a new array with your ranges, then iterate through the values and through the ranges inside. If the current value is inside the range, add the record to the current range:
<?php
$numbers = [1,1,2,2,2,3,4,4,5,6,6,6,7];
$counts = [];
$counts[] = ["values"=> [1, 3], "records" => []]; // first value in "values" is min, second is max
$counts[] = ["values"=> [4, 5], "records" => []];
$counts[] = ["values"=> [6, 7], "records" => []];
foreach ($numbers as $number) {
foreach ($counts as $key => &$value) {
if ($number >= $value["values"][0] && $number <= $value["values"][1]) { // if between the range, add it to the records
$value["records"][] = $number;
}
}
}
echo "<pre>";
foreach ($counts as $count) {
echo $count["values"][0]." - ".$count["values"][1]." = ".count($count["records"])." elements<br>";
}
Demo
I think array_intersect() and range() with sizeof()/count() does a cleaner job for this task. It eliminates the double-conditional.
Code: (Demo)
function count_range($array, $min, $max) {
return sizeof(array_intersect($array, range($min, $max)));
}
$array = [1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 7];
echo count_range($array, 1, 3); // 6 from [1, 1, 2, 2, 2, 3]
echo count_range($array, 4, 4); // 2 from [4, 4]
echo count_range($array, 2, 7); // 11 from [2, 2, 2, 3, 4, 4, 5, 6, 6, 6, 7]
range() creates a whitelist array of one or more consecutive integers.
array_intersect() compares the input array and the whitelist array all at once.
sizeof() is just an alias of count() to determine how many elements were retained.

Regrouping values in a bi-dimensional array in php

How the good php developer I am not would solve this problem?
I spent quite some time figuring how to shorten a bi-dimensional array.
$expanded = [ [1, 1], [1, 4], [3, 5], [1, 3], [4, 1], [4, 2], [1, 2], [4, 7], [3, 5] ];
into :
$shortened = [ [1, 10], [4, 10], [3, 10];
What I want is, if the first value of two arrays is the same, merge them !
This is what got me the closest to what I want :
$expanded = [ [1, 1], [1, 4], [3, 5], [1, 3], [2, 1], [2, 2], [1, 2], [2, 7], [3, 5] ];
$len = count($expanded);
$shortened[0] = $expanded[0];
for ($i = 0; $i < $len; $i++) {
for ($j = 1; $j < $len-$i; $j++) {
if ($expanded[$i][0] == $expanded[$i+$j][0]) {
$shortened[$i][0] = $expanded[$i][0];
$shortened[][1] = $shortened[$i][1] + $expanded[$i+$j][1];
} else {
$shortened[$i+1] = $expanded[$i+$j];
}
}
}
$shortened = array_reduce(
$expanded,
function (array $carry, array $item) {
list($one, $two) = $item;
if (! isset($carry[$one])) {
$carry[$one] = [ $one, 0 ];
}
$carry[$one][1] += $two;
return $carry;
},
[]
);
I got this far in a few minutes:
$expanded = [ [1, 1], [1, 4], [3, 5], [1, 3], [4, 1], [4, 2], [1, 2], [4, 7], [3, 5] ];
foreach ($expanded as $part)
{
$temp[$part[0]] += $part[1];
}
foreach ($temp as $key => $value)
{
$shortened[] = [$key, $value];
}
echo '<pre>';
print_r($shortened);
echo '</pre>';
And this is the version I really used, because my PHP < 5.4:
$expanded = array(array(1, 1),
array(1, 4),
array(3, 5),
array(1, 3),
array(4, 1),
array(4, 2),
array(1, 2),
array(4, 7),
array(3, 5));
foreach ($expanded as $part)
{
$temp[$part[0]] += $part[1];
}
foreach ($temp as $key => $value)
{
$shortened[] = array($key, $value);
}
echo '<pre>';
print_r($shortened);
echo '</pre>';
The shorted version would look like this:
foreach ($expanded as $p) $t[$p[0]] += $p[1];
foreach ($t as $k => $v) $shortened[] = [$k,$v];

Find all possible unique combinations of elements of an array in PHP [duplicate]

This question already has answers here:
Finding the subsets of an array in PHP
(5 answers)
Closed 9 years ago.
I’m aware of several questions covering this topic (e.g. here), but none of them (at least from what I found) do what I need.
Say I have an array of 3 elements [1, 2, 3]. I need to find all the possible unique combinations (so excluding permutations, like here), including the ones of repeating elements. So the result should be:
[1]
[2]
[3]
[1, 1]
[1, 2]
[1, 3]
[2, 2]
[2, 3]
[3, 3]
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 2]
[1, 2, 3]
[1, 3, 3]
[2, 2, 2]
[2, 2, 3]
[2, 3, 3]
[3, 3, 3]
Excluding subsets like [3, 2, 1] or [2, 1, 3], that are the same thing as [1, 2, 3].
How can I achieve this?
Fast solution using recursion, probably not the best way to do it, but it gets the job done.
<?php
$arr = array(1,2,3);
$result = array();
function combinations($arr, $level, &$result, $curr=array()) {
for($i = 0; $i < count($arr); $i++) {
$new = array_merge($curr, array($arr[$i]));
if($level == 1) {
sort($new);
if (!in_array($new, $result)) {
$result[] = $new;
}
} else {
combinations($arr, $level - 1, $result, $new);
}
}
}
for ($i = 0; $i<count($arr); $i++) {
combinations($arr, $i+1, $result);
}
// TEST
foreach ($result as $arr) {
echo join(" ", $arr) . '<br>';
}
?>

Categories