PHP merge single array elements into unique array - php

I'm not sure how to work the title of this question, so sorry if it's a little confusing.
I have an array;
Array
(
[username] => Array
(
[0] => 'a'
[1] => 'b'
[2] => 'c'
[3] => 'd'
[4] => 'e'
[5] => 'f'
)
[email] => Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] =>
[5] =>
)
[level] => Array
(
[0] => 1
[1] => 1
[2] => 1
[3] => 1
[4] => 1
[5] => 1
)
[role] => Array
(
[0] => 2
[1] => 1
[2] => 1
[3] => 1
[4] => 2
[5] => 1
)
[password] => Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] =>
[5] =>
)
[id] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
)
But I want it in this format:
Array
(
[0] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
[1] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
[2] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
[3] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
[4] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
[5] => Array
(
[username] => 'a'
[id] => 'a'
[email] => 'a'
)
I can't seem to figure it out, arrays end too soon, or there ends up being too many indexes etc. Ideas?

I would loop through the array and reconstruct it like so:
$index_count = count($array['username']); // in your case, this is 6
$new_array = array();
for ($i = 0; $i < $index_count; $i++) {
$new_array[] = array(
'username' => $array['username'][$i],
'email' => $array['email'][$i],
'id' => $array['id'][$i]
);
}
UPDATE If you want this to take into consideration any and all possible keys, try this:
$keys = array_keys($array);
if (count($keys)) {
$index_count = count($array[$keys[0]]);
$myArray = array();
for ($i = 0; $i < $index_count; $i++) {
$temp = array();
foreach($keys as $key) {
$temp[$key] = $array[$key][$i];
}
$myArray[] = $temp;
}
}

A different take, but here's what I would do - step by step:
First, let's get the keys - we'll be needing them a lot:
$result = array();//<-- this is the result array
$keys = array_keys($array);
Then, I'd get an empty, template array (somewhat like a model object)
$base = array_fill_keys($keys,array());
//array('username'=>array,'id'=>array(),...
Then, use that to build your result array:
$result = array_fill(0,count($array['username']),$base);
//In one-liner format, it looks like this:
$result = array_fill(0,count($array['username']),array_fill_keys(array_keys($array),array()));
//Works, but is messy as f***
Then just fill the lot, this is where that $keys variable pays off:
$length = count($result);
while ($key = array_shift($keys))
{
for ($i=0;$i<$length;$i++)
{
$result[$i][$key] = $array[$key][$i];
}
}
Note that I prefer using a while loop, as it is cleaner and (marginally) faster. Cleaner because that $keys array is being emptied as you go along. If you're working with substantial amounts of data it can sometimes make a difference. If the dataset is REALLY large, and the code has been thoroughly tested, you might even want to consider shifting from the source array (as it contains all data, it's a lot bigger than a an array containing just the keys):
while ($vals = array_shift($array))
{
$key = array_shift($keys);//<-- keep track of what array has been shifted
for ($i=0;$i<$length;$i++)
{
$result[$i][$key] = $vals[$i];
}
}
This neatly cleans up the source array, and the keys. I've tested this last approach on my server, writecodeonline and codepad, all with exactly the same results:
$foo = array('bar'=>array_fill(0,2,'ás'),'quar'=>range('a','z'));
$keys = array_keys($foo);
while($vals = array_shift($foo))
{
$key = array_shift($keys);
echo $key.' contains: => '.implode(', ',$vals).'<br/>';
}
bar contains: => ás, ás
quar contains: => a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z

Related

Concatenate value elements of two or more different arrays in PHP

I originally have this array
Array
(
[0] => Amministrativo ^25^9,11,2,10,18,4,7,^17,13,^0.75^0^0.25
[1] => Logico deduttive^7^^21,^0.75^0^-0.25
[2] => Situazionali^8^^20,^0.75^0^0.375
)
Using the function explode and array_diff i can get to this
Array
(
[0] => Amministrativo
[1] => 25
[2] => 9,11,2,10,18,4,7,
[3] => 17,13,
[4] => 0.75
[5] => 0
[6] => 0.25
)
Array
(
[0] => Logico deduttive
[1] => 7
[2] =>
[3] => 21,
[4] => 0.75
[5] => 0
[6] => -0.25
)
Array
(
[0] => Situazionali
[1] => 8
[2] =>
[3] => 20,
[4] => 0.75
[5] => 0
[6] => 0.375
)
but I would like to concatenate the elements of each array to get a unique array. I think I need to use the array_map function but I don't know how. This below is the result I would like to achieve
Array (
[0] => Amministrativo Logico deduttive Situazionali
[1] => 25 7 8
[2] => 9,11,2,10,18,4,7,
[3] => 17,13,21,20,
[4] => 0.75 0.75 0.75
[5] => 0 0 0
[6] => 0.25 -0.25 0.375
)
tnks
EDIT:
I tried the code that is here and it is fine.
But now I realized that there is also the problem that the arrays can be in variable number 1, 2, 3 or more and I can't know it before, I should adapt this code
$result = array_map(function ($item1, $item2,$item3) {
return "$item1 $item2 $item3";
}, $test[0], $test[1],$test[2]);
The lines of the original array are split with explode. The result is a two-dimensional array. This is transposed (swapping rows and columns). Then the lines are reassembled with implode.
function array_transpose(array $a) {
$r = array();
foreach($a as $keyRow => $subArr) {
foreach($subArr as $keyCol => $value) $r[$keyCol][$keyRow] = $value;
}
return $r;
}
$arr = [
'Amministrativo ^25^9,11,2,10,18,4,7,^17,13,^0.75^0^0.25',
'Logico deduttive^7^^21,^0.75^0^-0.25',
'Situazionali^8^^20,^0.75^0^0.375',
];
foreach($arr as $i => $row){
$arr[$i] = explode('^',$row);
}
$arr = array_transpose($arr);
foreach($arr as $i => $row){
$arr[$i] = implode(' ',$row);
}
var_export($arr);
Demo: https://3v4l.org/IdsDh

Find all possible permutations without repeating the value

Let's say I have this set of arrays as input:
[
0 => [1,2,4,5],
1 => [2,3,4],
2 => [1,3],
]
I would like to find all permutations possible selecting one value from each array. That value would be unique in the final result, so it won't repeat. For example, I can't have 1 twice in the result.
The count of arrays on input is the same as count of arrays on output.
Examples of combinations wanted (key=>value):
[0 => 1,1 => 2,2 => 3]
[0 => 2,1 => 3,2 => 1]
[0 => 5,1 => 2,2 => 1]
[0 => 1,1 => 3,2 => null]
Wrong results
[0 => 1,1 => 2,2 => 1]
or
[0 => 2,1 => 2,2 => 3]
I would like to get set of all possible permutations using PHP. How can I do that?
I have attached real data set http://pastebin.com/U6Hyawm4 However, I have no idea how many permutations there may be.
Here's a non-recursive version that's also optimized
/**
* Generates all the possible unique N-tuples from an array of N arrays of integers
*
* #param array $input
* #return array
*/
function generateCombinations(array &$input) {
// since the example results included [1, 3, null] I have assumed that
// null is a possible value of each set.
$sets = [];
foreach($input as $set) {
if(!in_array(null, $set)) {
$set[] = null;
}
$sets[] = $set;
}
// by working on the iterators of each array this loop
// linearizes the entire set of possible combinations
// and iterates it (skipping as many as it can).
$output = [];
$setCount = count($sets);
while(current($sets[0]) !== false) {
$testCombo = [];
for($setIdx = 0; $setIdx < $setCount; $setIdx++) {
if(!in_array(current($sets[$setIdx]), $testCombo)) {
$testCombo[] = current($sets[$setIdx]);
}
else {
// when a combination is thrown out as duplicate
// iterate to skip any other combo's that would also
// contain that duplicate
iterateSets($sets, $setIdx);
break;
}
}
// if there were no duplicates add it to the output and iterate
if(count($testCombo) == $setCount) {
$output[] = $testCombo;
iterateSets($sets, $setCount - 1);
}
}
return $output;
}
/**
* Iterates to the next potentially valid combination. I think of
* this like doing long-hand addition. Add 1 and carry is akin to
* next and reset.
*
* #param array $sets
* #param $index
*/
function iterateSets(array &$sets, $index) {
// reset iterators of all sets past the current one to skip
// combos that cannot be valid
for($i = $index + 1, $ic = count($sets); $i < $ic; $i++) {
reset($sets[$i]);
}
// always move one on current set
next($sets[$index]);
while($index > 0 && current($sets[$index]) === false) {
// wrap if current set is at the end
reset($sets[$index]);
$index--;
// move one for the preceding set
next($sets[$index]);
// then repeat
}
}
The resulting array is:
[
[1,2,3]
[1,2,null]
[1,3,null]
[1,4,3]
[1,4,null]
[1,null,3]
[2,3,1]
[2,3,null]
[2,4,1]
[2,4,3]
[2,4,null]
[2,null,1]
[2,null,3]
[4,2,1]
[4,2,3]
[4,2,null]
[4,3,1]
[4,3,null]
[4,null,1]
[4,null,3]
[5,2,1]
[5,2,3]
[5,2,null]
[5,3,1]
[5,3,null]
[5,4,1]
[5,4,3]
[5,4,null]
[5,null,1]
[5,null,3]
[null,2,1]
[null,2,3]
[null,3,1]
[null,4,1]
[null,4,3]
]
Here's an inefficient version:
$input = array(
[1,2,4,5],
[2,3,4],
[1,3]
);
function appendUnique($subs, $i) {
global $input;
if ($i == count($input)) {
return $subs;
}
$output = array();
foreach ($subs as $sub) {
foreach ($input[$i] as $v) {
$new_sub = array_values($sub);
if (in_array($v, $sub)) {
$new_sub[] = null;
} else {
$new_sub[] = $v;
}
$output[] = $new_sub;
}
}
return appendUnique($output, $i+1);
}
$output = appendUnique([[]], 0);
$output_json = array();
foreach ($output as $row) {
$output_json[] = json_encode($row);
}
$output_json = array_unique($output_json);
$deduped = array();
foreach ($output_json as $json) {
$deduped[] = json_decode($json);
}
print_r($deduped);
outputs:
Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] =>
)
[1] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[2] => Array
(
[0] => 1
[1] => 3
[2] =>
)
[3] => Array
(
[0] => 1
[1] => 4
[2] =>
)
[4] => Array
(
[0] => 1
[1] => 4
[2] => 3
)
[5] => Array
(
[0] => 2
[1] =>
[2] => 1
)
[6] => Array
(
[0] => 2
[1] =>
[2] => 3
)
[7] => Array
(
[0] => 2
[1] => 3
[2] => 1
)
[8] => Array
(
[0] => 2
[1] => 3
[2] =>
)
[9] => Array
(
[0] => 2
[1] => 4
[2] => 1
)
[10] => Array
(
[0] => 2
[1] => 4
[2] => 3
)
[11] => Array
(
[0] => 4
[1] => 2
[2] => 1
)
[12] => Array
(
[0] => 4
[1] => 2
[2] => 3
)
[13] => Array
(
[0] => 4
[1] => 3
[2] => 1
)
[14] => Array
(
[0] => 4
[1] => 3
[2] =>
)
[15] => Array
(
[0] => 4
[1] =>
[2] => 1
)
[16] => Array
(
[0] => 4
[1] =>
[2] => 3
)
[17] => Array
(
[0] => 5
[1] => 2
[2] => 1
)
[18] => Array
(
[0] => 5
[1] => 2
[2] => 3
)
[19] => Array
(
[0] => 5
[1] => 3
[2] => 1
)
[20] => Array
(
[0] => 5
[1] => 3
[2] =>
)
[21] => Array
(
[0] => 5
[1] => 4
[2] => 1
)
[22] => Array
(
[0] => 5
[1] => 4
[2] => 3
)
)

PHP merge arrays in specific order, not simply append them

I have two arrays:
Array ( [0] => a [1] => b [2] => c [3] => d )
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
I want to merge them so they end up like this:
Array ( [0] => a [1] => 1 [2] => b [3] => 2 [4] => c [5] => 3 [6] => d [7] => 4 )
Something like array 1[1], array 2[1], array 1[2], array 2[2], etc.
It's probably simple but I can't find an answer anywhere!
You would have to use a loop as far as I know, based on the length of the longest array.
// input arrays
$array1 = array( 1, 2, 3, 4 );
$array2 = array( 'a', 'b', 'c', 'd' );
// output arrays
$array3 = array();
// get the longest for the loop
$length = count($array1) > count($array2)? count($array1) : count($array2);
// if there is an element set for the index append to the output array
for ( $i=0; $i<$length; $i++ ){
if ( isset($array1[$i]) ) $array3[] = $array1[$i];
if ( isset($array2[$i]) ) $array3[] = $array2[$i];
}
print_r( $array3 );
Results in:
Array ( [0] => 1 [1] => a [2] => 2 [3] => b [4] => 3 [5] => c [6] => 4 [7] => d )
Run in this PHP Fiddle.

Comparison of two strings in PHP, and array_slice

I feel like I've done something very silly, but I can't work out what it is.
I'm running through an array of arrays generated by fgetcsv(). What I want is to split the array in two at the spot where the value in one line differs from the one in the next.
This code results in $first holding all but one of the arrays, and $second holding the last - completely ignoring the string comparison. strcmp() results in the same thing.
$csv = array();
$file = fopen('A.csv', 'r');
while (($result = fgetcsv($file)) !== false)
{
$csv[] = $result;
}
fclose($file);
array_shift($csv); //gets rid of the CSV headers
$rows = count($csv);
for ($i = 0; $i <= $rows; $i++){
if ($csv[$i][1] != $csv[$i+1][1]){
$first = array_slice($csv, 0, $i);
$second = array_slice($csv, $i);
}
}
Here's an example of the CSV file:
NAME,MATCHNAME,CHROMOSOME,START LOCATION,END LOCATION,CENTIMORGANS,MATCHING SNPS
A,person_one,2,20945970,23287731,2.48,500
A,person_one,2,233444593,234432885,1.56,500
A,person_one,4,99184637,100861136,1.24,500
A,person_two,1,154990798,157871980,2.8,700 //Here's where the new array should start
A,person_two,1,67136078,70785393,2.28,800
EDIT: My expected $first for this example would be:
Array
(
[0] => Array
(
[0] => A
[1] => person_one
[2] => 2
[3] => 20945970
[4] => 23287731
[5] => 2.48
[6] => 500
)
[1] => Array
(
[0] => A
[1] => person_one
[2] => 2
[3] => 233444593
[4] => 234432885
[5] => 1.56
[6] => 500
)
[2] => Array
(
[0] => A
[1] => person_one
[2] => 4
[3] => 99184637
[4] => 100861136
[5] => 1.24
[6] => 500
)
)
And my expected $second would be:
Array
(
[0] => Array
(
[0] => A
[1] => person_two
[2] => 1
[3] => 154990798
[4] => 157871980
[5] => 2.8
[6] => 700
)
[1] => Array
(
[0] => A
[1] => person_two
[2] => 1
[3] => 67136078
[4] => 70785393
[5] => 2.28
[6] => 800
)
)
It is doing what you want, but when it gets to the last iteration, it compares whatever's in the last line with null, so it overwrites $first and $second.
Try adding break; after the assignments to break out of the loop when the condition is met.

Better method of finding every possible combination of the partitioned sets

I need to find every possible combination of N sets of X length with no duplicates and in a particular order, e.g.
input: [["A"], ["B"], ["C"]]
output: [["A","B","C"],["A","B"],["A","C"],["B","C"],["A"],["B"],["C"]]
Rules:
The number or size of partitions is not fixed.
Only one member from each partition in each combination.
Combinations with more members are higher precedence.
Members earlier on in the input are higher precedence than members later on.
Another example with larger sets:
input: [["A","B"],["C","D","E"],["F"]]
output: [["A","C","F"],["A","D","F"],["A","E","F"],["B","C","F"],["B","D","F"],["B","E","F"],["A","C"],["A","D"],["A","E"],["B","C"],["B","D"],["B","E"],["A","F"],["B","F"],["C","F"],["D","F"],["E","F"],["A"],["B"],["C"],["D"],["E"],["F"]]
I've managed to get the output I want by combining the output of a power set function with a cartesian product function, but the resulting code isn't very concise or pretty. I was wondering if this could be better done with recursion?
Here's what I have already:
$test = json_decode('[["A"]]');
$test1 = json_decode('[["A"], ["B"], ["C"]]');
$test2 = json_decode('[["A", "B"], ["C", "D", "E"], ["F"]]');
/**
* Returns a power set of the input array.
*/
function power_set($in, $minLength = 1) {
$count = count($in);
$members = pow(2,$count);
$return = array();
for ($i = 0; $i < $members; $i++) {
$b = sprintf("%0".$count."b",$i);
$out = array();
for ($j = 0; $j < $count; $j++) {
if ($b[$j] == '1') {
$out[] = $in[$j];
}
}
if (count($out) >= $minLength) {
$return[] = $out;
}
}
return $return;
}
/**
* Returns the cartesian product of the input arrays.
*/
function array_cartesian() {
$_ = func_get_args();
if(count($_) == 0) {
return array(array());
}
$a = array_shift($_);
$c = call_user_func_array(__FUNCTION__, $_);
$r = array();
foreach($a as $v) {
foreach($c as $p) {
$r[] = array_merge(array($v), $p);
}
}
return $r;
}
/**
* Used with usort() to sort arrays by length, desc.
* If two arrays are the same length, then a sum of
* their keys is taken, with lower values coming first.
*/
function arraySizeDesc($a, $b) {
if(count($a) === count($b)) {
if(array_sum($a) === array_sum($b)) {
return 0;
}
return (array_sum($a) > array_sum($b)) ? 1 : -1;
}
return (count($a) < count($b)) ? 1 : -1;
}
/**
* Calculates a powerset of the input array and then uses
* this to generate cartesian products of each combination
* until all possible combinations are aquired.
*/
function combinations($in) {
$out = array();
$powerSet = power_set(array_keys($in));
usort($powerSet, 'arraySizeDesc');
foreach($powerSet as $combination) {
if(count($combination) < 2) {
foreach($in[$combination[0]] as $value) {
$out[] = array($value);
}
} else {
$sets = array();
foreach($combination as $setId) {
$sets[] = $in[$setId];
}
$out = array_merge($out, call_user_func_array('array_cartesian', $sets));
}
}
return $out;
}
echo "input: ".json_encode($test2);
echo "<br />output: ".json_encode(combinations($test2));
I realise the size of the output could grow very rapidly, but the input should only usually contain 1-5 sets of 1-50 members, so it doesn't need to deal with massive sets.
Essentially, your approach has been to generate the power-set of the input and then post-process that to arrive at your desired output.
One can approach it differently by attempting to solve it directly. One solution that came to me is the following.
Given an input A = [ A1, ...], assume the problem is already solved for A \ A1. Call this solution S'. How can we transform S' to the solution S for the whole of A? This is essentially by making two copy of S'. We distribute elements of A1 into the first copy. Let's call the new sequence S''. Thus the solution to A becomes simply a concatenation of S'' and S'.
Algorithmically,
Input: A = [ A1, A2, ..., An]
combine(A, i, n):
if n < i
return []
if n == i
return singletons(Ai)
S' = combine(A, i + 1, n)
S'' = [a, S'], for each a in Ai
return [S'', S']
The function singletons returns a family of one-element subsets of the input set. So, if it take the input [1, 2, 3], it'll return [[1], [2], [3]].
The approach should be clear if you ignore some loose usage of concatenation of sequences here and there ...
Good luck!
Try
$test = array ();
$test [0] = json_decode ( '[["A"]]', true );
$test [1] = json_decode ( '[["A"], ["B"], ["C"]]', true );
$test [2] = json_decode ( '[["A", "B"], ["C", "D", "E"], ["F"]]', true );
echo "<pre>" ;
$set = array ();
getSet ( $test, $set );
$set = array_values(array_unique($set));
$return = powerSet($set,1,3);
print_r ($return);
Output
Array
(
[0] => Array
(
[0] => F
)
[1] => Array
(
[0] => E
)
[2] => Array
(
[0] => E
[1] => F
)
[3] => Array
(
[0] => D
)
[4] => Array
(
[0] => D
[1] => F
)
[5] => Array
(
[0] => D
[1] => E
)
[6] => Array
(
[0] => D
[1] => E
[2] => F
)
[7] => Array
(
[0] => C
)
[8] => Array
(
[0] => C
[1] => F
)
[9] => Array
(
[0] => C
[1] => E
)
[10] => Array
(
[0] => C
[1] => E
[2] => F
)
[11] => Array
(
[0] => C
[1] => D
)
[12] => Array
(
[0] => C
[1] => D
[2] => F
)
[13] => Array
(
[0] => C
[1] => D
[2] => E
)
[14] => Array
(
[0] => B
)
[15] => Array
(
[0] => B
[1] => F
)
[16] => Array
(
[0] => B
[1] => E
)
[17] => Array
(
[0] => B
[1] => E
[2] => F
)
[18] => Array
(
[0] => B
[1] => D
)
[19] => Array
(
[0] => B
[1] => D
[2] => F
)
[20] => Array
(
[0] => B
[1] => D
[2] => E
)
[21] => Array
(
[0] => B
[1] => C
)
[22] => Array
(
[0] => B
[1] => C
[2] => F
)
[23] => Array
(
[0] => B
[1] => C
[2] => E
)
[24] => Array
(
[0] => B
[1] => C
[2] => D
)
[25] => Array
(
[0] => A
)
[26] => Array
(
[0] => A
[1] => F
)
[27] => Array
(
[0] => A
[1] => E
)
[28] => Array
(
[0] => A
[1] => E
[2] => F
)
[29] => Array
(
[0] => A
[1] => D
)
[30] => Array
(
[0] => A
[1] => D
[2] => F
)
[31] => Array
(
[0] => A
[1] => D
[2] => E
)
[32] => Array
(
[0] => A
[1] => C
)
[33] => Array
(
[0] => A
[1] => C
[2] => F
)
[34] => Array
(
[0] => A
[1] => C
[2] => E
)
[35] => Array
(
[0] => A
[1] => C
[2] => D
)
[36] => Array
(
[0] => A
[1] => B
)
[37] => Array
(
[0] => A
[1] => B
[2] => F
)
[38] => Array
(
[0] => A
[1] => B
[2] => E
)
[39] => Array
(
[0] => A
[1] => B
[2] => D
)
[40] => Array
(
[0] => A
[1] => B
[2] => C
)
)
Functions used
function powerSet($in, $minLength = 1, $max = 10) {
$count = count ( $in );
$members = pow ( 2, $count );
$return = array ();
for($i = 0; $i < $members; $i ++) {
$b = sprintf ( "%0" . $count . "b", $i );
$out = array ();
for($j = 0; $j < $count; $j ++) {
if ($b {$j} == '1')
$out [] = $in [$j];
}
if (count ( $out ) >= $minLength && count ( $out ) <= $max) {
$return [] = $out;
}
}
return $return;
}
function getSet($array, &$vals) {
foreach ( $array as $key => $value ) {
if (is_array ( $value )) {
getSet ( $value, $vals );
} else {
$vals [] = $value;
}
}
return $vals;
}

Categories