PHP all combinations into array - php

Let's say I have two variables, strings converted to arrays
$VAR_1 = '1-1:2-1';
$VAR_1 = Explode(':', $VAR_1);
$VAR_2 = '3-1:4-1:2-2:2-3:2-4';
$VAR_2 = Explode(':', $VAR_2);
Now I want 'all' combinations of VAR_1 and VAR_2 mixed set into a new array $COMB
But at least 1, or both variable(s) of VAR_1 must be in the new array, and the combined string must consists of 5 variables.
I need ALL combinations, also duplicate, but which are in a different order.
How to achieve this ?
Scenario: Poker Game (VAR_1 = player cards (color - number) VAR_2 = table cards)

{
$array1 = array(0 => 'zero_a', 2 => 'two_a', 3 => 'three_a');
$array2 = array(1 => 'one_b', 3 => 'three_b', 4 => 'four_b');
$result = $array1 + $array2;
var_dump($result);
}

First, you need to create an array of all 7 variables, using simple
$arr3 = $arr1 + $arr2;
or
$arr3 = array_merge($arr1, $arr2);
Then, you need to select 5-element groups, that means 2 element will always be skipped out, these 2 elements will have indexes $i and $j, making 2 nested for cycles an excelent solution for this problem, here's an example:
$array = array("1-2", "1-5", "1-8", "3-4", "2-1", "2-2", "1-6");
$result = array(); //not sure if this line is nesseserly, but better be sure
for ($i = 0; $i < 7; $i++) {
for ($j = $i + 1; $j <= 7; $j++) {
$buffer = array(); //this is nesseserly
for ($k = 0; $k < 7; $k++) {
if ($k == $i || $k == $j) {
continue;
}
$buffer[] = $array[$k]; //add to end
}
$result[] = $buffer; //add to end
}
}
var_dump($result);
This works wine for me, tested.

Use array_merge()
http://php.net/manual/en/function.array-merge.php
$a = array("a", "b", "c");
$b = array("g", "a", "f");
$c = array_merge($a, $b);
var_dump($c); //array("a", "b", "c", "g", "a", "f");

Related

How can I return all combinations of a given string? (ex. 'foo bar' = bar, bar_foo, foo)

This question is not a duplicate of the suggested question above. The title may sound similar, but its answer does not in any way lead to the result described in the question below.
I'm having a hard time recursively iterating through an array of unknown length to create unique combinations of strings. Can you help?
The goal is to take a string like foo bar and create unique combinations from this string:
foo
bar
bar_foo (alphabetized to make unique combinations, not permutations)
Another example:
car bar add should return:
add
add_bar
add_car
add_bar_car
bar
bar_car
car
Here's my progress:
function string_builder($length) {
$arrWords = array('add','bar','car','dew','eat','fat','gym','hey','ink','jet','key','log','mad','nap','odd','pal','qat','ram','saw','tan','urn','vet','wed','xis','yap','zoo');
$arr = array();
for ($i=0; $i < $length; $i++) {
$arr[] = $arrWords[$i];
}
return implode(' ', $arr);
}
function get_combinations($string) {
$combinations = array(); // put all combinations here
$arr = explode(' ',$string);
$arr = array_unique($arr); // only unique words are important
sort($arr); // alphabetize to make unique combinations easier (not permutations)
$arr = array_values($arr); // reset keys
for ($i=0; $i < count($arr); $i++) {
// this is where I'm stuck
// how do I loop recursively through all possible combinations of an array?
}
return $combinations;
}
// Test it!
for ($i=1; $i < 26; $i++) {
$string = string_builder($i);
$combinations = get_combinations($string);
echo $i . " words\t" . count($combinations) . " combinations\t" . $string . "\n";
// print_r($combinations);
}
Another attempt:
function getCombinations2($str, $min_length = 2) {
$words = explode(' ', $str);
$combinations = array();
$len = count($words);
for ($a = $min_length; $a <= $min_length; $a++) {
for ($pos = 0; $pos < $len; $pos ++) {
if(($pos + $a -1) < $len) {
$tmp = array_slice($words, $pos, $a);
sort($tmp);
$tmp = implode('_',$tmp);
$combinations[] = $tmp;
}
}
}
$combinations = array_unique($combinations);
return $combinations;
}
You can know you're successful when you print out the combinations and look for a couple combinations that should be there (for example, "fat_zoo","car_tan"). Both of my attempts will show several of these, but never all of them.
What you are searching is pretty easy to build up (and explain) using binary numbers.
Each position in the binary word should indicate whether a certain word out of the array is appended - or not.
let's assume, you are having an array made out of two words:
$words = ["foo","bar"];
You are now expecting the combinations
foo
bar
bar_foo
in binary this can be represented as
1 0
0 1
1 1
With three words $words = ["foo","bar", "baz"]; it would be the combinations
foo
bar
baz
foo_bar
foo_baz
bar_baz
foo_bar_baz
which can be interpreted as
1 0 0
0 1 0
0 0 1
1 1 0
1 0 1
0 1 1
1 1 1
(ignoring alphabetic sorting as of now)
let's move those binary numbers into a concrete order and look at the decimal value of them:
0 0 1 // dec 1
0 1 0 // dec 2
0 1 1 // dec 3
1 0 0 // dec 4
1 0 1 // dec 5
1 1 0 // dec 6
1 1 1 // dec 7
To Note: The number of elements you want to generate is (2^n)-1, where n is your number of words.
And that's basically all you need to do:
Iterate from 1 to (2^n)-1.
Take the binary version of that decimal number as "array-indexes".
Append elements where index is "1".
php:
print_r(get_combinations("car bar add"));
function get_combinations($str) {
$words = explode(' ',$str);
$elements = pow(2, count($words))-1;
$result = array();
for ($i = 1; $i<=$elements; $i++){
$bin = decbin($i);
$padded_bin = str_pad($bin, count($words), "0", STR_PAD_LEFT);
$res = array();
for ($k=0; $k<count($words); $k++){
//append element, if binary position says "1";
if ($padded_bin[$k]==1){
$res[] = $words[$k];
}
}
sort($res);
$result[] = implode("_", $res);
}
sort($result);
return $result;
}
results in:
Array
(
[0] => add
[1] => bar
[2] => bar_add
[3] => car
[4] => car_add
[5] => car_bar
[6] => car_bar_add
)
You can sort the array $res alphabetically before imploding it.
Limited to length 3:
print_r(get_combinations("car bar add"));
function get_combinations($str) {
$words = explode(' ',$str);
$elements = pow(2, count($words))-1;
$result = array();
for ($i = 1; $i<=$elements; $i++){
$bin = decbin($i);
$padded_bin = str_pad($bin, count($words), "0", STR_PAD_LEFT);
$res = array();
for ($k=0; $k<count($words); $k++){
//break, if maximum length is reached.
if (count($res) == 3){
break;
}
//append element, if binary position says "1";
if ($padded_bin[$k]==1){
$res[] = $words[$k];
}
}
sort($res);
//check result array if combination already exists before inserting.
$res_string =implode("_", $res);
if (!in_array($res_string, $result)){
$result[] = $res_string;
}
}
sort($result);
return $result;
}
If you are using PHP 5.5 you can use generators to eliminate your memory problem. It also lowers the overall execution time a bit.
Generators
15: 256kb, 0.48s
16: 256kb, 0.90s
19: 256kb, 7.76s
Original
15: 5.75mb, 0.49s
16: 17.25mb, 0.99s
19: 86.24mb, 8.58s
Based on dognose's function you can replace the $result[] assignment to yield $res instead. Rather than running through the whole loop and returning a gigantic array, the function steps through it one by one, returning (yielding) a single element each time.
function combo_gen($str)
{
$words = explode(' ',$str);
$elements = pow(2, count($words))-1;
$result = array();
for ($i = 1; $i<=$elements; $i++)
{
$bin = decbin($i);
$padded_bin = str_pad($bin, count($words), "0", STR_PAD_LEFT);
$res = array();
for ($k=0; $k<count($words); $k++){
//append element, if binary position says "1";
if ($padded_bin[$k]==1){
$res[] = $words[$k];
}
}
sort($res);
$res = implode("_", $res);
yield $res;
}
}
foreach(combo_gen('one two three') as $item) {
//stuff
}}
There might be a more elegant solution, but I did it with 2 functions.
The getCombosOfLength function gives every $intLength combination within the array. The GetCombos function just runs GetCombosOfLength for each length that you want. This works very well to generate all combinations that are 1-5 items. If you run it for all 25-item combinations, it has some problems.
$a = array("c", "b", "f", "v", "g", "e", "h", "i", "j", "k", "l", "m", "n", "p", "o", "r", "a", "q", "s", "t", "u", "w", "x", "y", "z");
$b = getCombos($a, 5);
print "<pre>\n";
print_r($b);
function getCombos($arrInput, $intMax = null) {
sort($arrInput);
if (is_null($intMax)) $intMax = count($arrInput);
$arrOutput = array();
for ($i = $intMax; $i > 0; $i--) {
$arrReturn = getCombosOfLength($arrInput, $i);
for ($j = 0; $j < count($arrReturn); $j++) $arrOutput[] = $arrReturn[$j];
}
return $arrOutput;
}
function getCombosOfLength($arrInput, $intLength) {
$arrOutput = array();
if ($intLength == 1) {
for ($i = 0; $i < count($arrInput); $i++) $arrOutput[] = array($arrInput[$i]);
return $arrOutput;
}
$arrShift = $arrInput;
while (count($arrShift)) {
$x = array_shift($arrShift);
$arrReturn = getCombosOfLength($arrShift, $intLength - 1);
for ($i = 0; $i < count($arrReturn); $i++) {
array_unshift($arrReturn[$i], $x);
$arrOutput[] = $arrReturn[$i];
}
}
return $arrOutput;
}

Remove child array from array where child item is the last of that item

My brain is broken. Here's the concept:
$a = array('please', 2);
$s = array('help', 3);
$d = array('me', 1);
$f = array('!', 3);
$g = array('asdf', 1);
$myArray = array($a, $s, $d, $f, $g);
for ($i = 0; $i < count($myArray); $i++) {
// code here
}
Code should contain logic that removes indexes 0, 3, and 4 from myArray because they contain the last value of their kinds (last 2, 3, and 1 respectively) at position $myArray[$i][1].
--EDIT:
Adapted suggestions from comments to get this:
$a = array('please', 2);
$s = array('help', 3);
$d = array('me', 1);
$f = array('!', 3);
$g = array('asdf', 1);
$myArray = array($a, $s, $d, $f, $g);
$used = array();
for ($i = 0; $i < count($myArray); $i++) {
$used[$myArray[$i][1]] = $i;
}
foreach ($used as $deleteMe)
unset($myArray[$deleteMe]);
$newArray = array_values($myArray);
var_dump($newArray);
It works, and creating a new array with array_values eliminates the gaps created by using unset().
$i = count($myArray);
$used = array();
while ($i-- > 0)
if (!isset($used[$myArray[$i][1]])) {
$used[$myArray[$i][1]] = true;
unset($myArray[$i]);
}
This simple snippet first iterates from the back over the array and deletes the index if an entry with this number wasn't deleted yet.
Iterate through $myArray, save last position of every kind in $otherArray, loop through $otherArray unseting items from $myArray. That's all.

combination with array elements

I have this code:
$um = array("PHP", "JAVA", "MySQL")
$a = count($um)
for ($i = 0; $i < $a; $i++) {
for ($x = $i + 1; $x <$a; $x++) {
$arr1 [] = array($um[$i],$um[$x]);
}
}
This will output something like these combinations:
PHP[0] JAVA[1]
PHP[0] MySQL[2]
JAVA[1] MySQL[2]
Well, works without any problem.
But now, i want to change the output to something like:
PHP[0] JAVA[1]
JAVA[1] MySQL[2]
MySQL[2] PHP[0]
The logic will be the same for the three array elements or even 10, and so on.
Any idea about this ?
Just use modular arithmetic: you want to pair indices (i,i+1), where the i+1 wraps around to 0 if it becomes too large.
for ($i = 0; $i < $a; $i++) {
echo $um[$i], ', ', $um[($i+1) % $a]
}
So for $a=3 this displays according to indices:
0, 1
1, 2
2, 0 (since 3 % 3==0)
Modulus Operator docs
// make a copy of the array
$array2 = $array;
// rotate the entries by one
$array2[] = array_shift($array2);
// combine the elements
$combined = array_map(function () { return func_get_args(); }, $array, $array2);

PHP: Take several arrays, and make new ones based on shared indexes? [duplicate]

This question already has answers here:
Transposing multidimensional arrays in PHP
(12 answers)
Is there a php function like python's zip?
(14 answers)
Closed 10 months ago.
So, imagine you have 3 arrays:
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
And you want to combine them into new arrays based on index:
1,6,11
2,7,12
3,8,13
4,9,14
5,10,15
What on earth could achieve this? Also, the total number of arrays is not known.
EDIT: Here's a snippet of my code so far (pulling data from a DB):
<?php
$ufSubmissions = $wpdb->get_results( $wpdb->prepare("SELECT * FROM wp_user_feedback WHERE user = '$ufUser' ORDER BY date DESC") );
$cleanedResponses = array();
foreach ($ufSubmissions as $submission) {
$cleanedResponses[] = unserialize($submission->responses);
}
array_map(null, $cleanedResponses));
?>
Doesn't seem to be working though, even $cleaned responses is an array of arrays.
Mostly like Alex Barrett's answer, but allows for an unknown number of arrays.
<?php
$values = array(
array(1,2,3,4,5),
array(6,7,8,9,10),
array(11,12,13,14,15),
);
function array_pivot($values)
{
array_unshift($values, null);
return call_user_func_array('array_map', $values);
}
print_r(array_pivot($values));
If your arrays are all the same length, you can pass as many as you want to the array_map function with null as the callback parameter.
array_map(null,
array(1, 2, 3, 4, 5),
array(6, 7, 8, 9, 10),
array(11, 12, 13, 14, 15));
The above will return the following two-dimensional array:
array(array(1, 6, 11),
array(2, 7, 12),
array(3, 8, 13),
array(4, 9, 14),
array(5, 10, 15));
This is a documented trick, so quite safe to use.
$ret = array();
for ($i =0; $i < count($input[0]); $i++){
$tmp = array();
foreach ($input as $array) {
$tmp[] = $array[$i];
}
$ret[] = $tmp;
}
Em... What's the problem? If they are equal sized, then you do
<?php
$a = array(1,2,3,4,5);
$b = array(6,7,8,9,10);
$c = array(11,12,13,14,15);
$d = array();
for ($i = 0; $i < sizeof($a); $i++) {
$d[] = array($a[$i], $b[$i], $c[$i]);
}
var_dump($d);
This is not tested, read it to get the idea instead of paste it.
The point is to put everything alltoghether in a feed and then redistribute it onto new arrays of a max length, the last one could not be full.
<?php
// initial vars
$max_size = 3; // of the new arrays
$total_array = $a + $b + $c; // the three arrays summed in the right order
$current_size = length($total_array);
$num_of_arrays = ceil($current_size / $max_size);
// redistributing
$result_arrays = array();
for($i = 0; $i < $num_of_arrays; $i++){ // iterate over the arrays
$new_array= array();
for($t = 0; $t < $max_size){
$pos = $num_of_arrays * $t + $i;
if(isset($total_array[$pos]) {
$new_array[] = $total_array[$pos];
}
}
$result_arrays[] = $new_array;
}
?>
// This takes an unlimited number of arguments and merges into arrays on index
// If there is only 1 argument it is treated as an array of arrays
// returns an array of arrays
function merge_on_indexes () {
$args = func_get_args();
$out = array();
if (count($args) == 1) for ($i = 0; isset($args[0][$i]); $i++) for ($j = 0; isset($args[0][$i][$j]); $j++) $out[$j][] = $args[0][$i][$j]; else for ($i = 0; isset($args[$i]); $i++) for ($j = 0; isset($args[$i][$j]); $j++) $out[$j][] = $args[$i][$j];
return $out;
}
// Usage examples
// Both return array('data1','data3','data5'),array('data2','data4','data6')
$arr1 = array('data1','data2');
$arr2 = array('data3','data4');
$arr2 = array('data5','data6');
$result = merge_on_indexes($arr1,$arr2);
print_r($result);
$multiDimArr = array(
array('data1','data2'),
array('data3','data4'),
array('data5','data6')
);
$result = merge_on_indexes($multiDimArr);
print_r($result);
$arr = get_defined_vars(); //gets all your variables
$arrCount = 0;
$arrOfarrs = array();
foreach($arr as $var){ //go through each variable
if(is_array($var)){ //and see if it is an array
$arrCount++; //we found another array
for($i == 0;$i < count($var); $i++){ //run through the new array
$arrOfarrs[$i][] == $var[$i]; //and add the corresponding elem
}
}
}

Repeat array to a certain length?

I'm having an array for example with 4 elements array("a", "b", "c", d"); what is the fastest way to repeat this array to create a new array with a certain length, e.g 71 elements?
// the variables
$array = array("a", "b", "c", "d");
$desiredLength = 71;
$newArray = array();
// create a new array with AT LEAST the desired number of elements by joining the array at the end of the new array
while(count($newArray) <= $desiredLength){
$newArray = array_merge($newArray, $array);
}
// reduce the new array to the desired length (as there might be too many elements in the new array
$array = array_slice($newArray, 0, $desiredLength);
Solution using SPL InfiniteIterator:
<?php
function fillArray1($length, $values) {
foreach (new InfiniteIterator(new ArrayIterator($values)) as $element) {
if (!$length--) return $result;
$result[] = $element;
}
return $result;
}
var_dump(fillArray(71, array('a', 'b', 'c', 'd')));
The real SPL hackers might have dropped the if (!$length--) break; and instead used a limit iterator: new LimitIterator(new InfiniteIterator(new ArrayIterator($values)), 0, $length), but I thought that to be overkill...
A simple solution using each() and reset() and the array's internal pointer:
<?php
$array = array('a', 'b', 'c', 'd');
$length = 71;
$result = array();
while(count($result) < $length)
{
$current = each($array);
if($current == false)
{
reset($array);
continue;
}
$result[] = $current[1];
}
echo count($result); // Output: 71
In order to join this club:
$result = call_user_func_array('array_merge', array_fill(0, ceil($size/count($array)), $array));
while(count($result) > $size) array_pop($result);
You asked for the fastest so I did a benchmark (Source: http://pastebin.com/G5w7QJPU)
Kau-Boy: 5.40128803253
Frxstrem: 5.00970411301
NikiC: 4.12150001526
user2469998: 0.561513900757
Alexander: 1.92847204208
Hammerite: 2.17130494118
Max: 12.9516701698
Evert: 1.9378361702
Christoph: 1.6862449646
Test took 35.7696909904s
user2469998 is the fastest but it only works for string values with single chars (or the same length if you use second parameter of str_split).
$newarray = array();
$i = 0;
$oldarrayvalues = array_values($oldarray);
$oldarraysize = count($oldarrayvalues);
if ( $oldarraysize ) {
while ( count($newarray) < DESIRED_ARRAY_SIZE ) {
$newarray[] = $oldarrayvalues[$i];
$i++;
$i %= $oldarraysize;
}
}
If you have PHP 5.3 available, you can also try this:
function fill(array $initalArray, $toCount) {
$initialArrayCount = count($initalArray);
$fillUp = function(array $filledUpArray, $missingCount)
use(&$fillUp, $initalArray, $initialArrayCount, $toCount)
{
if($missingCount <= 0) return array_slice($filledUpArray, 0, $toCount);
return $fillUp(array_merge($filledUpArray, $initalArray), $missingCount - $initialArrayCount);
};
return $fillUp($initalArray, $toCount - $initialArrayCount);
}
$theArray = array("a", "b", "c", "d");
$toLength = 71;
$filledArray = fill($theArray, $toLength);
print_r($filledArray);
<?php
$array = array('a', 'b', 'c', 'd');
$end = 71;
$new_array = array();
while(count($new_array) <= $end)
{
foreach($array as $key => $value)
{
$new_array[] = $value;
}
}
$new_array = array_slice($new_array, 0, $end);
Tested and works.
You can test for yourself by adding this:
echo '<pre>';
print_r($new_array);
echo '</pre>';
$array = array("a", "b", "c", "d");
$merge = array();
$desiredLength = 71;
while(2 * count($array) <= $desiredLength){
$array = array_merge($array, $array);
}
if($desiredLength > count($array))
$merge = array_slice($array, 0, $desiredLength - count($array));
$array = array_merge($array, $merge);
$array = array_slice($array, 0, $desiredLength);
print_r($array);
$arr = array("a", "b", "c", "d");
$len = 71;
$a = array();
$a = str_split( substr( str_repeat( join( $arr), ceil( $len / count( $arr))), 0, $len));
var_export($a);
I think that user2469998 was closest but just not that nice.
For my example, I use pipe to implode and the str_repeat function to build a string that meets the length, explode it back apart and trim the fat.
$list = array('a','b','c','d');
$length = 6;
$result = array_slice(explode('|', str_repeat(implode('|', $list).'|',ceil($length/count($list)))), 0, $length);
Many ways to achieve this but thought I'd share mine. The only restriction is that you need to use a character to implode and explode on which isn't part of the array items or the exploder won't work properly.
:)

Categories