I'm iterating over a directory and merging the JSON-Files in it. But it doesn't quite work as i want it to. The array I get after iterating over 3 files is just the last file. Somewhere along the way it seems to just overwrite the previous files. I'm not sure where though. Also I would like to remove rows with certain entries, but I'd be happy if the merging would work at least.
<?php
$dir = new DirectoryIterator("path1");
$destination = "path2";
$json = file_get_contents($destination);
$result = json_decode($json,true);
foreach ($dir as $fileinfo) {
if (!$fileinfo->isDot()) {
$path = $dir -> getPathname();
$data = file_get_contents($path);
echo $data; //works as intended. Prints 3 different Arrays after eachother
$current = json_decode(file_get_contents($path),true);
$result = array_merge($result,$current);
}
}
$final = json_encode($result);
file_put_contents($destination,$final);
?>
Thanks in advance for any help
The function array_merge has this overwriting behaviour, as specified in the manual:
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
This effect can be illustrated with this little example:
$a1 = array("a" => 1, "b" => 2);
$a2 = array("a" => 100, "b" => 200);
$result = array_merge($a1, $a2);
print_r (json_encode($result));
output:
{"a":100,"b":200}
So, the values of the first array are lost.
There are several solutions, but it depends on which result you would like to get. If for instance you would like to get this:
{"a":[1, 100],"b":[2, 200]}
Then use the function array_merge_recursive instead of array_merge.
If you prefer to get this:
[{"a":1,"b":2},{"a":100,"b":200}]
Then use this code:
$result[] = $a1;
$result[] = $a2;
In your original code, that last solution would look like this:
$result[] = json_decode($json,true);
foreach ($dir as $fileinfo) {
// ...
$result[] = $current;
// ...
}
Related
I am collecting data from a sheet using PHP. I am losing information because if there is any empty cell, Google removes the whole row.
$array refers to some data collected from Google Sheets:
$range = "Sheet1!A1:C99";
$response = $service->spreadsheets_values->get($spreadsheetId, $range);
$array = $response->getValues();
But some cells in the sheet are empty, and if any of those are in the left or right border of the row, Google will remove all the data in that row. [2] => [3].
There is a question similar to this one, however, it is not a PHP example and I cannot apply it here.
The Range.getValues() method will return a 2D array of values. You could look at it as a matrix more than an array, because all the rows will have the same number of "column" elements (Even if they are empty in the sheet). So the culprit is somewhere else XD.
With that in mind, try something like the following:
$keys = array_shift($array); // Extract & exclude header row from $array
/*
Combine keys to each row in $array */
foreach ($array as &$row){
$row = array_combine($keys,$row);
};
print_r($array);
Update:
As stated in Resource: ValueRange's values[] field: "For output, empty trailing rows and columns will not be included." So you really can't count on using that method for the purpose you are looking for.
As an alternative, you may want to use Apps Script, and the Range.getValues() method I mentioned above.
I paid someone to fix this and this is what he did:
$range = "Sheet1!A2:C99";
$response = $service->spreadsheets_values->get($spreadsheetId, $range);
$values = $response->getValues();
$response = [];
foreach ($values as $value) {
$data = [
'animal' => null,
'descripcion' => null,
'peso' => null
];
if (isset($value[0])) {
$data['animal'] = $value[0];
}
if (isset($value[1])) {
$data['descripcion'] = $value[1];
}
if (isset($value[2])) {
$data['peso'] = $value[2];
}
$response[] = $data;
}
echo '<pre>';
print_r($response);
echo '</pre>';
It did work!
Thank you
I have an array like this,
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
I want to find any value with an ">" and replace it with a range().
The result I want is,
array(
1,2,3,4,5,6,7,8,9,10,11,12, '13.1', '13.2', 14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
);
My understanding:
if any element of $array has '>' in it,
$separate = explode(">", $that_element);
$range_array = range($separate[0], $separate[1]); //makes an array of 4 to 12.
Now somehow replace '4>12' of with $range_array and get a result like above example.
May be I can find which element has '>' in it using foreach() and rebuild $array again using array_push() and multi level foreach. Looking for a more elegant solution.
You can even do it in a one-liner like this:
$array = array(1,2,3,'4>12','13.1','13.2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,#range(...array_slice(explode(">","$c>$c"),0,2)));},
[]
));
I avoid any if clause by using range() on the array_slice() array I get from exploding "$c>$c" (this will always at least give me a two-element array).
You can find a little demo here: https://rextester.com/DXPTD44420
Edit:
OK, if the array can also contain non-numeric values the strategy needs to be modified: Now I will check for the existence of the separator sign > and will then either merge some cells created by a range() call or simply put the non-numeric element into an array and merge that with the original array:
$array = array(1,2,3,'4>12','13.1','64+2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,strpos($c,'>')>0?range(...explode(">",$c)):[$c]);},
[]
));
See the updated demo here: https://rextester.com/BWBYF59990
It's easy to create an empty array and fill it while loop a source
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$res = [];
foreach($array as $x) {
$separate = explode(">", $x);
if(count($separate) !== 2) {
// No char '<' in the string or more than 1
$res[] = $x;
}
else {
$res = array_merge($res, range($separate[0], $separate[1]));
}
}
print_r($res);
range function will help you with this:
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$newArray = [];
foreach ($array as $item) {
if (strpos($item, '>') !== false) {
$newArray = array_merge($newArray, range(...explode('>', $item)));
} else {
$newArray[] = $item;
}
}
print_r($newArray);
I have multiple arrays that contain multiple elememts, for example:
$array1 = (1,2)
$array2 = (3,4)
I need to generate all possible combinations for elements of arrays. For given example, the output should be:
1_3, 1_4, 2_3, 2_4
However, the issue is that both number of arrays and amount of elements in each one can be different. So we can also take something like that:
$array1 = (1,2,3,4)
$array2 = (5,6,7)
$array3 = (8,9,10,11,12)
and output should look like that:
1_5_8, 1_5_9, 1_5_10 etc. until 4_7_12.
How do I implement something like that? I know that I should use foreach loop, but I have no idea what to do if amount of "foreaches" can be different every time I execute algorithm.
Any help is really appreciated :)
<?php
$array1 = [1,2,3,4];
$array2 = [5,6,7];
$array3 = [8,9,10,11,12];
$collection = [$array1,$array2,$array3];
$result = $collection[0];
array_shift($collection);
foreach($collection as $each_array){
$temp = [];
foreach($result as $each_sub_result){
foreach($each_array as $each_item){
$temp[] = $each_sub_result."_".$each_item;
}
}
$result = $temp;
}
print_r($result);
Algorithm:
We collect all arrays in our $collection variable.
Now, we loop over all elements of our $collection variable where each individual item is an array.
I have done an array_shift() since we are assigning first element of $collection to $result(so we don't want to iterate over them again).
We maintain $temp to store temporary results. Note that we use $result also to store temporary results, so that we can iterate over it and get new temporary results. By temporary, I mean building the final array. For example: It starts with 1, then 1_5 and then finally 1_5_8.
After the looping completes, we will have our final answer in $result.
try something like this:
$array1 = array(1,2,3);
$array2 = array(4,5,6);
foreach ($array1 as $a1){
foreach ($array2 as $a2){
echo $a1 . '_' . $a2 . ', ';
}
}
You can expand it like you wish.
I'm good in JS so that's why I did the algo in JS, you can try to do this in PHP,,no much difficult. You just have to pass in all the array is the function and the rest should be done by itself. Let me know if any confusions.
var arr1 = [1, 2, 3];
var arr2 = [4, 5];
var arr3 = [6, 7, 8];
var numberOfArrays = -1;
function processArrays(concatWith = [], allArrays) {
const duplicateArray = [...allArrays];
for (var i = 0; i < allArrays.length; i++) {
const currentArray = duplicateArray.splice(0, 1);
currentArray[0].forEach(function (value) {
concatWith.push(value);
processArrays(concatWith, duplicateArray);
if (concatWith.length === numberOfArrays) {
console.log(concatWith.join('_'));
}
concatWith.pop();
});
}
}
function makeCombinations(arrayOfAllArrays = []) {
numberOfArrays = arrayOfAllArrays.length;
processArrays([], arrayOfAllArrays);
}
makeCombinations([arr1, arr2, arr3]);
Suppose that I have 2 files:
File1.txt
10;30;15;40;12;14;15
23;32;10;50;12;54;60
File2.txt
2;4;5;6;7;8;9
3;6;7;8;9;0;7
I want to subtration between these 2 files. Ex 10 - 2........
PHP code:
$file1 = 'File1.txt';
$file2 = 'File2.txt';
if(file_exists($file1)){
$files = fopen($file1,'r');
while(!feof($files)){
$data = explode(";",fgets($files));
if($title ==""){
$title = $data[0];
}
if(!empty($data[3])){
$cate = $data[3];
$filepmta = fopen($file2,'r');
while(!feof($filepmta)){
$hourData = explode(";",fgets($filepmta));
if(!empty($hourData[3])){
if($title ==""){
$title = $hourData[0];
}
if(!empty($btnHour)){
echo $percentRed = ((int)$data[2] - (int)$hourData[2]);
//it loads page so long time so I don know what's error.
}
}
}
It loads the page is so long time.I don know how to fix this,Anyone know help me please,thanks.
I would recommend just simply opening both files and storing the contents of the file into memory. IE Create 2 simple loops (not nested) which iterates through both files respectively and saves the content to an array.
Then make one more loop after the first two. Iterate through the third loop based on the array size of the first two. (I'm assuming you can assume they are both the same size, but it will be a good idea to add checks to make sure loop 1 and loop 2 produce the same size/length arrays.
Within the 3rd loop iterate through both previously two generated arrays with the same index.
Pseudo Code // even tho I said I'd do pseudo code, I ended up doing it all. :-/
$file_values1 = $file_values2 = $answers = array();
while(!feof($file1)) $file_values1[] = explode(';', fgets($file)); // loop 1
while(!feof($file2)) $file_values2[] = explode(';', fgets($file)); // loop 2
foreach($file_values1 as $key1 => $sub_array) { // loop 3
foreach($sub_array as $key2 => $value) // loop 4; hehe, oh wellz
{
$answers[] = $file_values1[$key1][$key2] = $file_values2[$key1][$key2]
}
}
Basically, the nested loop 4, meh. There ARE faster answers. The 3rd loop can be improved upon, it's O(N^2). I'll leave that for you to optimize. No free lunches. :-)
fgets() doesn't move the file pointer after the last line, so you're staying on the last line, feof() still returns false and you're trapped in an infinite loop.
The common way of doing what you want is:
while (($line = fgets($handle)) !== false) {
echo $line;
}
Have a look at the code example in fgets() documentation, it's quite exhaustive.
You're making this way more complicated than it has to be.
Open your files and read them both in with file(), which will give you an array of lines in each file.
Iterate over both line arrays simultaneously with a MultipleIterator.
Convert each lines' contents to arrays with str_getcsv() and an array_map() trick.
Iterate over each line values with another MultipleIterator.
Compute the subtraction and print it.
That's all it takes! I've implemented the above algorithm below.
$file1 = 'File1.txt';
$file2 = 'File2.txt';
$file1 = file( $file1);
$file2 = file( $file2);
$iter = new MultipleIterator;
$iter->attachIterator( new ArrayIterator( $file1));
$iter->attachIterator( new ArrayIterator( $file2));
foreach( $iter as $element) {
list( $line1, $line2) = array_map( 'str_getcsv', $element, array( ';', ';'));
$val_iter = new MultipleIterator;
$val_iter->attachIterator( new ArrayIterator( $line1));
$val_iter->attachIterator( new ArrayIterator( $line2));
foreach( $val_iter as $value) {
list( $el1, $el2) = $value;
echo ($el1 - $el2); echo ";";
}
echo "\n";
}
You can see from this demo, which statically defines the files as arrays, that this produces:
8;26;10;34;5;6;6;
20;26;3;42;3;54;53;
I would like to take an array, say:
array("one", "two", "three");
and make it into an array of arrays, like:
$someArray["one"]["two"]["three"];
The array of arrays ($someArray) would have to be created by some sort of loop as the initial array is created by an explode() so i don't know how deep $someArray would be.
I hope this is clear, thanks all for your time!
I am reading up on it at the moment myself, would array_map() work for this?
You can do this:
$path = array("one", "two", "three");
$ref = &$arr;
foreach ($path as $key) {
if (!isset($ref[$key])) break;
$ref = &$ref[$key];
}
At the end $ref is a reference to $arr['one']['two']['three'] if it exists. And if you replace break by $ref[$key] = array() you will build an array with that structure instead.
You should use array_reduce.
The solution is quite simple.
To do the trick, reverse the array and only then apply the reduction.
$a = array('a','b','c');
$x = array_reduce(array_reverse($a), function ($r, $c) {
return array($c=>$r);
},array());
EDIT an expanded and explained version:
In php we don't have a function to go deep into an array automatically but we haven't to.
If you start from the bottom, we will be able to enclose an array into the previous one
with a simple assignation.
$a = array('a','b','c');
$result=array(); // initially the accumulator is empty
$result = array_reduce(
array_reverse($a),
function ($partialResult, $currentElement) {
/* enclose the partially computed result into a key of a new array */
$partialResult = array($currentElement=>$partialResult);
return $partialResult;
},
$result
);
By the way I prefer the shorter form. I think it is a functional idiom and doesn't need further explanation to a middle experienced developer (with a bit of functional background). The second one add a lot of noise that, it is suitable to learn but source
of distraction into production code (obviously I'm referring to function with just a return statement).
i'm not sure why you would want this.
But but the basis of what your wanting would be like this
$numbers = array("one", "two", "three");
$array = array();
$pointer = &$array;
$last_val = NULL;
foreach($numbers as $key => $val){
if(!empty($pointer[$last_val])){
$pointer = &$pointer[$last_val];
$pointer = new array($val);
$last_val = $val;
}else{
$pointer = new array($val);
$last_val = $val;
}
}
This should then right what you want to $array;
This is untested but i think that should work if it does not just tell me what its doing and i will have a look
$arrNew = array();
$ref = &$arrNew;
$arrParts = array("one", "two", "three");
foreach($arrParts as $arrPart){
$ref[$arrPart] = array();
$ref = &$ref[$arrPart];
}
When you print $arrNew, It will print this..
Array
(
[one] => Array
(
[two] => Array
(
[three] => Array
(
)
)
)
)