Combine multiple arrays - php

I have arrays like this
Array 1:
Array
(
[0] => Array
(
[0] => 0
[1] => -0.025
)
[1] => Array
(
[0] => 0
[1] => -0.025
)
[2] => Array
(
[0] => 0
[1] => -0.025
)
)
Array 2:
Array
(
[0] => Array
(
[0] => 0
[1] => -0.025
)
[1] => Array
(
[0] => 0
[1] => -0.025
)
)
Array 3:
Array
(
[0] => Array
(
[0] => 0
[1] => -0.025
)
[1] => Array
(
[0] => 0
[1] => -0.025
)
[2] => Array
(
[0] => 0
[1] => -0.025
)
[3] => Array
(
[0] => 0
[1] => -0.025
)
)
and more of them.
I want to combine them to this
Array
(
[0] => Array
(
[0] => 0
[1] => -0.025
[2] => 0
[3] => -0.025
[4] => 0
[5] => -0.025
)
[1] => Array
(
[0] => 0
[1] => -0.025
[2] => 0
[3] => -0.025
[4] => 0
[5] => -0.025
)
[2] => Array
(
[0] => 0
[1] => -0.025
[2] => 0
[3] => 0
[4] => 0
[5] => -0.025
)
[3] => Array
(
[0] => 0
[1] => 0
[2] => 0 // These (0-3) are 0 because the other two arrays haven't 3 in the first level
[3] => 0
[4] => 0
[5] => -0.025
)
)
The arrays have a different number of entries in the first level. In the second level there are always 2 entries.
In the second level of the combined array the first two keys (0 and 1) should always have the entries from the first array. The second two keys (2 and 3) should always have the entries from the second array and so on. In my example there are 30 arrays that I want to combine.
If an array does have more first level entries than others all entries should have 0 as value.
I hope you understand this :)

I built it ;-/
Working demonstration at eval.in
Requirements:
Basically it is transposing rows into columns.
the output columns all have the length of the longest array.
The source arrays can have different lengths. Any empty entries are assumed to have a value of array(0, 0).
The output columns are converted into a single dimensional array. i.e. source value arrays are appended to the column array.
Explanation:
I decided to use the internal iterator that all arrays have. i.e. the arrays already can record there own state such as current position (row) that they are on.
After that it is just 'housekeeping':
keep a list of which arrays still have entries to be processed. When none of them have then we are finished.
record which output column ($mergedEntryNo) that we are creating.
scan along each (row) reading entries for the current row.
after each row: advance to the next row and record which arrays are active - if any.
The class (SimpleMerge) that does the work
class SimpleMerge {
protected $sources = array(); // a list of arrays!
protected $sourceCount = 0; // useful
protected $isActive = array(); // which of them have entries to process.
public $merged = array(); // output in here and public
protected $anyActiveSources = false;
// need a list of arrays
public function __construct(array $allSources)
{
$this->sources = $allSources;
$this->sourceCount = count($allSources);
$this->isActive = array_fill(0, $this->sourceCount, true);
}
// generate the output by scanning the arrays line by line
public function generateOutput()
{
$this->generateInit();
$mergedEntryNo = 0;
while ($this->anyActiveSources) {
// set the next output entries
for ($sourceNo = 0; $sourceNo < $this->sourceCount; $sourceNo++) {
$this->addEntry($mergedEntryNo, $this->getEntry($sourceNo));
}
$mergedEntryNo++;
$this->nextPassAdvance();
$this->setIsActiveSource();
}
return $this->merged;
}
// ensure everything is initialized correctly
public function generateInit()
{
$this->merged = array();
foreach ($this->sources as &$source) {
reset($source); // force internal iterators to the start
$this->merged[] = array(); // empty arrays in the output
}
unset($source);
$this->setIsActiveSource();
}
// add to output
public function addEntry($mergedNo, array $values)
{
foreach ($values as $value) {
$this->merged[$mergedNo][] = $value;
}
}
// get the current source entry - will be array of zeroes if end of array
public function getEntry($sourceNo)
{
if ($this->isActive[$sourceNo]) {
return current($this->sources[$sourceNo]);
}
else {
return array(0, 0);
}
}
// check and set which ones are still active and also indicate if any are active
public function setIsActiveSource()
{
$activeCount = 0;
for ($sourceNo = 0; $sourceNo < $this->sourceCount; $sourceNo++) {
$isActive = current($this->sources[$sourceNo]) !== false;
$this->isActive[$sourceNo] = $isActive;
$activeCount = $activeCount + ($isActive ? 1 : 0);
}
$this->anyActiveSources = $activeCount > 0;
}
// advance iterators on the sources that are still active
public function nextPassAdvance()
{
for ($sourceNo = 0; $sourceNo < $this->sourceCount; $sourceNo++) {
if ($this->isActive[$sourceNo]) { // was last time
next($this->sources[$sourceNo]);
}
}
}
}
Run it
// create and run the generator...
$mergeAll = new SimpleMerge($allSourcesList);
$merged = $mergeAll->generateOutput();
Output:
Note, there are no zero values in the input. The values indicate source table and entry number.
Outpt: Array
(
[0] => Array
(
[0] => 11
[1] => -11.025
[2] => 21
[3] => -21.025
[4] => 31
[5] => -31.025
[6] => 41
[7] => -41.025
)
[1] => Array
(
[0] => 12
[1] => -12.025
[2] => 22
[3] => -22.025
[4] => 32
[5] => -32.025
[6] => 42
[7] => -42.025
)
[2] => Array
(
[0] => 13
[1] => -13.025
[2] => 0
[3] => 0
[4] => 33
[5] => -33.025
[6] => 43
[7] => -43.025
)
[3] => Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 34
[5] => -34.025
[6] => 44
[7] => -44.025
)
)

Related

Split an array into a MD array every nth line

bit of a question here. Ive got an array that contains data which has been parsed from a website using all our favourite php functions such as array_map.
The array is current 3 sub arrays deep.
Here is the code I am using:
for ($tcid = 1; $tcid <= count($categories); $tcid++) {
$catHeader[$tcid] = $categories[$tcid][0];
$event[$i]['tickets'] = $categories;
unset($categories[$tcid][0]);
$categories[$tcid] = array_map('trim', $categories[$tcid]);
$categories[$tcid] = array_values($categories[$tcid]);
$ab = 0;
for ($b = 0; $b <= count($categories[$tcid]); $b++) {
if ($categories[$tcid][$b] == "" || !$categories[$tcid][$b] || $categories[$tcid][$b] == null) {
unset($categories[$tcid][$b]);
}
}
}
and the array looks something like....
[1] => Array (
[data] => Array ( ...
)
[tickets] => Array (
[1] => Array (
[0] => xxx
[1] => etc
[3] => etc2
)
[2] => (
[0] => Std1
[1] => 10 / 10
[2] => £20.00
[3] => £200.00
[4] => Std2
[5] => 100 / 100
[6] => £13.00
[7] => £1,300.00
[8] => Std3
[9] => 10 / 320
[10] => £15.00
[11] => £150.00
)
)
)
My question to you today, is how on earth do I split the array every 4 \n's or array keys as they're known and explode each 4 into a further sub array?
So that Std1, Std2, Std3 will be their own sub array with its associated data of the 2nd key of tickets, but also doing this for every sub array of tickets that has more than 1 set of data (a set of data being 4 array keys).
I've tried all sorts but can't get it to work.
See below of how I want it to look.
[1] => Array (
[data] => Array ( ...
)
[tickets] => Array (
[1] => Array (
[0] => xxx
[1] => etc
[3] => etc2
)
[2] => (
[0] => Array (
[0] => Std1
[1] => 10 / 10
[2] => £20.00
[3] => £200.00
)
[1] => Array (
[0] => Std2
[1] => 100 / 100
[2] => £13.00
[3] => £1,300.00
)
[2] => Array (
[0] => Std3
[1] => 10 / 320
[2] => £15.00
[3] => £150.00
)
)
)
)
Thanks
As noted in the comments, you'd be best off handling your array by-reference to modify it's original contents somewhere within your loops
Provided the array groupings you want to chunk are in groups of 4, you could array_chunk() it:
$array['tickets'][2] = array_chunk($array['tickets'][2], 4);

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 array - Sum value of the same key when key are number [duplicate]

This question already has answers here:
PHP Array Group by one field and Sum up two fields [duplicate]
(2 answers)
Closed 4 months ago.
My situation is similar to this thread :
Associative array, sum values of the same key
However in my case all keys are number.
I would like to reduce / combine array where key 0 is similar and make a sum of all other keys.
Here is my original array :
Array
(
[0] => Array
(
[0] => 093042
[1] => 3
[2] => 0
[4] => 0
)
[1] => Array
(
[0] => 222032
[1] => 0
[2] => 13
[4] => 0
)
[2] => Array
(
[0] => 222032
[1] => 0
[2] => 0
[4] => 15
)
[3] => Array
(
[0] => 152963
[1] => 45
[2] => 0
[4] => 0
)
[4] => Array
(
[0] => 222032
[1] => 0
[2] => 7
[4] => 0
)
)
and here is the output i need :
Array
(
[0] => Array
(
[0] => 093042
[1] => 3
[2] => 0
[4] => 0
)
[1] => Array
(
[0] => 222032
[1] => 0
[2] => 20
[4] => 15
)
[2] => Array
(
[0] => 152963
[1] => 45
[2] => 0
[4] => 0
)
)
The solution of other thread is not working because they use the key name and i don't know how i can adapt this to my situation.
Please if you can give me an example of working solution.
REPLY :
For now i try something like that : Take from other thread
$sum = array_reduce($data, function ($a, $b) {
if (isset($a[$b[0]])) {
$a[$b[0]]['budget'] += $b['budget'];
}
else {
$a[$b[0]] = $b;
}
return $a;
});
But this example look is only for key named budget but in my case is number and i have 3 key [1] [2] [3] how can't sum key 1,2,4 where key 0 is similar
This should work for you:
Basically I just loop through your array and check if there is already an element in $result with the key of the first element of $v. If not I initialize it with an array_pad()'ed array of 0's + the current array of the iteration of the foreach loop.
And after this I loop through each element of $v expect the first one and add it to the result array.
At the end I just reindex the result array with array_values().
<?php
foreach($arr as $v){
if(!isset($result[$v[0]]))
$result[$v[0]] = array_pad([$v[0]], count($v), 0);
$count = count($v);
for($i = 1; $i < $count; $i++)
$result[$v[0]][$i] += $v[$i];
}
$result = array_values($result);
print_r($result);
?>
output:
Array
(
[0] => Array
(
[0] => 093042
[1] => 3
[2] => 0
[3] => 0
)
[1] => Array
(
[0] => 222032
[1] => 0
[2] => 20
[3] => 15
)
[2] => Array
(
[0] => 152963
[1] => 45
[2] => 0
[3] => 0
)
)
try this
$data = Array
(
0 => Array
(
0 => 93042,
1 => 3,
2 => 0,
4 => 0,
),
1 => Array
(
0 => 222032,
1 => 0,
2 => 13,
4 => 0,
),
2 => Array
(
0 => 222032,
1 => 0,
2 => 0,
4 => 15,
),
3 => Array
(
0 => 152963,
1 => 45,
2 => 0,
4 => 0,
),
4 => Array
(
0 => 222032,
1 => 0,
2 => 7,
4 => 0,
),
);
var_dump($data);
// grouping
$tab1 = array();
foreach ($data as $e) {
if (!isset($tab1[$e[0]])) {
$tab1[$e[0]] = array();
}
$tab1[$e[0]][] = $e;
}
//var_dump($tab1);
// summing
$tabSum = array();
foreach ($tab1 as $id => $t) {
foreach ($t as $e) {
unset($e[0]);
if (!isset($tabSum[$id])) {
$tabSum[$id] = $e;
} else {
foreach ($e as $key => $value) {
$tabSum[$id][$key] += $value;
}
}
}
}
var_dump($tabSum);

php array filtering values defined by key from multiple arrays

Hello I'm trying to solve the following issue. I have some code which is self explanatory but I need to add a couple of lines to it
I would like filter the lower value arrays defined by the key value (in this case [2]) via the key value [1]. So if I have 3 arrays which contain key [1] with a value 100, then the arrays should be filtered via key [2].
Example of my code so far:
foreach($data as $line) {
if(substr($line,0,1)=="A") {
if(!$first) {
$parts = explode(chr(9), $line);
list($num1, $num2) = explode('_', $parts[1]); //code comes first / tested and works
$parts[2] = isset($num2) ? $num2 : $parts[2]; //it replaces key[2] with _* (1,2,3)
//then this will follow
$pos = strpos($parts[1], '_'); // this will remove all _* from key [1] if they exist
if($pos !== false) $parts[1] = substr($parts[1], 0, $pos); // tested and works
//echo "<pre>"; print_r($parts); echo "</pre>";
//need code to filter the arrays defined by key [1] via key [2] here?
So for example if I have multiple arrays after my piece of code like this:
Array
(
[0] => A
[1] => 100
[2] => 1
[3] => 1184
[4] => 0
)
Array
(
[0] => A
[1] => 100
[2] => 2
[3] => 1185
[4] => 0
)
Array
(
[0] => A
[1] => 100
[2] => 3
[3] => 1186
[4] => 0
)
Array
(
[0] => A
[1] => 101
[2] => 1
[3] => 1187
[4] => 0
)
Array
(
[0] => A
[1] => 101
[2] => 2
[3] => 1188
[4] => 0
)
Array
(
[0] => A
[1] => 302
[2] => 0
[3] => 1161
[4] => 0
)
After some code to filter the arrays, the final example result will be:
Array
(
[0] => A
[1] => 100
[2] => 3
[3] => 1186
[4] => 0
)
Array
(
[0] => A
[1] => 101
[2] => 2
[3] => 1188
[4] => 0
)
Array
(
[0] => A
[1] => 302
[2] => 0
[3] => 1161
[4] => 0
)
Please I could do with some help on this it only needs a couple of lines, I'm not a programmer but I'd like to finish this project.
Try this :
$array = array("A", "100_1", 0, 1184, 0);
$array = array_map(
function($str) {
return preg_replace_callback('/([\d]+)\_([1-3])/', function($matches){ return $matches[1] + $matches[2]-1;}, $str);
},
$array
);
print_r($array)
Try this:
$part = array();
foreach ($parts as $key => $value) {
if (isset($part[$value[1]])) {
if ($parts[$part[$value[1]]][0][2] < $value[0][2]) {
$part[$value[1]] = $key;
}
} else {
$part[$value[1]] = $key;
//echo "<pre>"; print_r($part); echo "</pre>";
}
}
I change the answer because it was not what I'm looking for but this is.

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.

Categories