Reputation: 4109
Consider data.csv:
"1","2","3","4"
"5","6","7","8"
"9","10","11","12"
"13","14","15","16"
"17","18","19","20"
"21","22","23","24"
"25","26","27","28"
"29","30","31","32"
"33","34","35","36"
I need to read this CSV and concatenate every nth, say every 3rd row to each other. So the desired output is:
array (
[0] => 1,2,3,4,13,14,15,16,25,26,27,28 // row 1, 4 and 7
[1] => 5,6,7,8,17,18,19,20,29,30,31,32 // row 2, 5 and 8
[2] => 9,10,11,12,21,22,23,24,33,34,35,36 // row 3, 6 and 9
)
I need n to be variable, so it should be easy to, for instance, concatenate every 4th row as well:
array (
[0] => 1,2,3,4,17,18,19,20,33,34,35,36 // row 1, 5 and 9
[1] => 5,6,7,8,21,22,23,24 // row 2, 6
[2] => 9,10,11,12,25,26,27,28 // row 3, 7
)
I now have this:
$path = "data.csv";
$row = 1;
if (($handle = fopen($path, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
for ($i = 1; $i <= 3; $i++) { // concatenate every 3rd row
if ($row % $i == 0) $newrows[$i] .= implode(",", $data);
}
$row++;
}
}
print_r($newrows);
But it doesn't work as expected since it outputs
Array (
[1] => 1,2,3,45,6,7,89,10,11,1213,14,15,1617,18,19,2021,22,23,2425,26,27,2829,30,31,3233,34,35,36
[2] => 5,6,7,813,14,15,1621,22,23,2429,30,31,32
[3] => 9,10,11,1221,22,23,2433,34,35,36
)
Do you have a better idea? This mathematical logic always has me puzzled! :-)
Upvotes: 5
Views: 702
Reputation: 8146
I don't have access to my server, right now, so I can't test it, but I think this simplified version of what you have should work.
$path = "data.csv";
$row = 1;
$groupings = 4; // group every n rows
if(($handle = fopen($path, "r")) !== FALSE)
{
while(($data = fgetcsv($handle, 1000, ",")) !== FALSE)
{
$where = $row % $groupings; // which grouping is this?
$newrows[$where] .= implode(",", $data).",";
$row++;
}
}
for($i = 0; $i < count($newrows); $i++)
{
$newrows[$i] = substr($newrows[$i], 0, -1);
}
You need to run a second loop AFTER the initial while-loop since there's no way (that I can think of) to detect if the data being added is the last data that's going to be added to that grouping.
Upvotes: 4
Reputation: 198247
You can iterate over all lines, calculate the remainder to the zero-based line-number (as it is the case with SplFileObject
) as new index and then fill an array
with the value.
In the following examle, the $perEach
array will be filled:
$each = 3; # must be greater than 0
$path = '../data/numbers.csv'; # path to the csv file
$file = new SplFileObject($path);
$file->setFlags(SplFileObject::DROP_NEW_LINE);
$perEach = array();
foreach ($file as $lineNumber => $line)
{
$index = $lineNumber % $each;
$isNew = empty($perEach[$index]);
if ($isNew) {
$perEach[$index] = $line;
} else {
$perEach[$index] .= ',' . $line;
}
}
The result with the data you've provided:
array(3) {
[0] => string(55) ""1","2","3","4","13","14","15","16","25","26","27","28""
[1] => string(55) ""5","6","7","8","17","18","19","20","29","30","31","32""
[2] => string(58) ""9","10","11","12","21","22","23","24","33","34","35","36""
}
In case the data-format is not that consistent and you need true CSV parsing, SplFileObject
can do that, too. It's probably even better because it allows to initialize the array first:
$file = new SplFileObject($path);
$file->setFlags(SplFileObject::READ_CSV);
$perEach = array_fill(0, $each, array());
foreach ($file as $lineNumber => $line)
{
$index = $lineNumber % $each;
$perEach[$index] = array_merge($perEach[$index], $line);
}
The result then naturally is as array to keep the values per each line apart:
array(3) {
[0]=>
array(12) {
[0]=>
string(1) "1"
[1]=>
string(1) "2"
[2]=>
string(1) "3"
[3]=>
string(1) "4"
[4]=>
string(2) "13"
[5]=>
string(2) "14"
[6]=>
string(2) "15"
[7]=>
string(2) "16"
[8]=>
string(2) "25"
[9]=>
string(2) "26"
[10]=>
string(2) "27"
[11]=>
string(2) "28"
}
[1]=>
array(12) {
[0]=>
string(1) "5"
[1]=>
string(1) "6"
[2]=>
string(1) "7"
[3]=>
string(1) "8"
[4]=>
string(2) "17"
[5]=>
string(2) "18"
[6]=>
string(2) "19"
[7]=>
string(2) "20"
[8]=>
string(2) "29"
[9]=>
string(2) "30"
[10]=>
string(2) "31"
[11]=>
string(2) "32"
}
[2]=>
array(12) {
[0]=>
string(1) "9"
[1]=>
string(2) "10"
[2]=>
string(2) "11"
[3]=>
string(2) "12"
[4]=>
string(2) "21"
[5]=>
string(2) "22"
[6]=>
string(2) "23"
[7]=>
string(2) "24"
[8]=>
string(2) "33"
[9]=>
string(2) "34"
[10]=>
string(2) "35"
[11]=>
string(2) "36"
}
}
Upvotes: 3