How to break an array and use part of it with PHP - php

I am new to this so your help is much needed. I have a CSV file and I need to modify the last column of this CSV. What I did is separated the columns in array using this code below.
<?php
function readCSV($csvFile){
$file_handle = fopen($csvFile,"r");
while (!feof($file_handle)) {
$line_of_text[] = fgetcsv($file_handle, 1024);
}
fclose($file_handle);
return $line_of_text;
}
$csvFile = 'testslash.csv';
$csv = readCSV($csvFile);
print_r($csv);
?>
This is the output i am getting which is fine .. what i want to end up with ex: array 0,2 and array 1,2. i tried array slice but with no avail. any other options?
Array
(
[0] => Array
(
[0] => ABC
[1] => XXX
[2] => 972 54 120000 / 129999
)
[1] => Array
(
[0] => DEF
[1] => XXX
[2] => 972 52 1100180 / 9
)
)

You're saying you want only a specific part of the subarrays.
This is easily done using a foreach loop and a new array.
$resultArray = array();
foreach($cvs as $row)
{
$resultArray[] = $row[2]; //if you dont care about the index of the array
$resultArray[$row[0]] = $row[2]; //if you want to speficy the index as the value of e.g. the first entry of the subarray;
}

Related

Creating an associative array from one-dimension array

Was not really sure on what question's title should be here...
Sample .csv:
tennis,soccer,sports
car,plane,things
jeans,shirt,things
My final, ideal, outcome should be an array that looks like this:
Array
(
[sports] => Array
(
[0] => tennis
[1] => soccer
)
[things] => Array
(
[0] => car
[1] => plane
[2] => jeans
[3] => shirt
)
)
Here is my most recent attempt to achieve the outcome above (after many tries):
<?php
$f_name = 'test.csv';
// Stores all csv data
$csv_data = array_map('str_getcsv', file($f_name));
$c = count($csv_data);
$tmp = array();
$data_for_email = array();
for ($i = 0; $i < $c; $i++) {
// Remove last element and make it a key
$le = array_pop($csv_data[$i]);
$tmp[$le] = $csv_data[$i];
$data_for_email = array_merge_recursive($data_for_email, $tmp); // MEMORY ERROR
}
print_r($data_for_email);
?>
This is what I get as a result:
Array
(
[sports] => Array
(
[0] => tennis
[1] => soccer
[2] => tennis
[3] => soccer
[4] => tennis
[5] => soccer
)
[things] => Array
(
[0] => car
[1] => plane
[2] => jeans
[3] => shirt
)
)
As you can see, I get duplicates of .csv's line 1 in [sports] array.
More detailed description of my requirement:
Each line has 3 fields.
3rd field becomes a key in a new associative array.
Two remaining fields (1st and 2nd) become values for that key.
Because multiple lines may (and do) contain identical 3rd field (while combination of 1st and 2nd fields are always different), I need to then merge all these duplicate keys' values into 1.
P.S. I could parse that array (to remove duplicate values) afterwards, but the real .csv file is large and it becomes too slow to process it, and I receive the following error at the line which I marked with // MEMORY ERROR:
Fatal Error: Allowed Memory Size of 134217728 Bytes Exhausted
I tried increasing the memory limit but I'd prefer to avoid this if possible.
Should be a little easier. No need for array_merge_recursive:
foreach($csv_data as $row) {
$key = array_pop($row);
if(!isset($data_for_email[$key])) {
$data_for_email[$key] = [];
}
$data_for_email[$key] = array_merge($data_for_email[$key], $row);
}
More memory efficient would be:
Not reading the whole file in memory. fgetcsv reads one line at a time
Avoiding a recursive merge
Code:
$handle = fopen($f_name, 'r');
if (!$handle) {
// Your error-handling
die("Couldn't open file");
}
$data_for_email = array();
while($csvLine = fgetcsv($handle)) {
// Remove last element and make it a key
$le = array_pop($csvLine);
if (isset($data_for_email[$le])) {
$data_for_email[$le] = array_merge($data_for_email[$le], $csvLine);
} else {
$data_for_email[$le] = $csvLine;
}
}
fclose($handle);
You just need to initialize $tmp in every loop which will resolve your problem. Check below code:
for ($i = 0; $i < $c; $i++) {
// Remove last element and make it a key
$le = array_pop($csv_data[$i]);
$tmp = []; //Reset here
$tmp[$le] = $csv_data[$i];
$data_for_email = array_merge_recursive($data_for_email, $tmp); // MEMORY ERROR
}
Hope it helps you.
Use the name for the key to get a unique list. It is cheaper than merge if there is a lot of data.:
$handle = fopen('test.csv', 'r');
$res = [];
while ($data = fgetcsv($handle)) {
list($first, $second, $type) = $data;
$res[$type] = ($res[$type] ?? []);
array_map(function($e)use(&$res, $type) {
$res[$type][$e] = $e;
}, [$first, $second]);
}
output:
Array
(
[sports] => Array
(
[tennis] => tennis
[soccer] => soccer
)
[things] => Array
(
[car] => car
[plane] => plane
[jeans] => jeans
[shirt] => shirt
)
)
i made something, too, but now the others were faster. :D
I've made it oop, it doesn't quite come out what you wanted but maybe it helps you further.
I have not come any further now, unfortunately, wanted to show it to you anyway :)
Here is your index.php ( or whatever the file is called. )
<?php
include "Data.php";
$f_name = 'in.csv';
// Stores all csv data
$csv_data = array_map('str_getcsv', file($f_name));
$c = count($csv_data);
$tmp = array();
$data_for_email = array();
foreach ($csv_data as $data){
$key = array_pop($data);
array_push($data_for_email,new Data($data,$key));
}
foreach ($data_for_email as $data){
array_push($tmp,$data->getValue());
}
foreach ($tmp as $value){
print_r($value);
echo "<br>";
}
and here the class Data:
<?php
class Data
{
private $value = [];
public function __construct($data, $key)
{
$this->value[$key]=$data;
}
/**
* #return array
*/
public function getValue()
{
return $this->value;
}
}
as output you bekome something like that:
Array ( [sports] => Array ( [0] => tennis [1] => soccer ) )
Array ( [things] => Array ( [0] => car [1] => plane ) )
Array ( [things] => Array ( [0] => jeans [1] => shirt ) )
ps:
surely there is another function that summarizes the same keys, but somehow i don't find anything now...
I hope it helps :)

Convert CSV into Array

I have a csv file that looks like this:
Did,status
"123","Active"
"456","Not-Active"
"789","Active"
....and so on
I would like to be able to convert it into an array that looks like this:
$DidStatus = array("123"=>"Active", "456"=>"Not-Active", "789"=>"Active");
I tried this but it's not what I'm looking for:
$file = file_get_contents("test.csv");
$data = array_map("str_getcsv", preg_split('/\r*\n+|\r+/', $file));
print_r($data);
but the output is not the one I'm looking for:
Array
(
[0] => Array
(
[0] => Did
[1] => status
)
[1] => Array
(
[0] => 123
[1] => Active
)
[2] => Array
(
[0] => 456
[1] => Not-Active
)
[3] => Array
(
[0] => 789
[1] => Active
)
[4] => Array
(
[0] =>
)
)
Look into fgetcsv()
<?php
$handle = fopen("test.csv", "r");
$result = Array();
fgetcsv($handle); //Removes the first line of headings in the csv
while($data = fgetcsv($handle)) {
$result[$data[0]] = $data[1];
}
print_r($result); //the result
?>
There are other ways to do it, but given your current code, just extract an array of the 1 values and index it by the 0 values.
unset($data[0]); //If needed to remove the header row
$data = array_column($data, 1, 0);
You may consider this as an alternate for the first step (not sure if FILE_IGNORE_NEW_LINES is absolutely necessary):
$data = array_map('str_getcsv', file('test.csv', FILE_IGNORE_NEW_LINES));
Look into fgetcsv. That is meant to be used for something like this.
$arr = array();
$file = fopen('test.csv', 'r');
while (($result = fgetcsv($file)) !== FALSE)
{
$arr[] = $result;
}
fclose($file);
You can use this package league/csv and you can find instructions on how to use it here - one of the first examples shows how to convert csv to array

How to merge different arrays in a while loop of feof() while using a CSV file?

I am retrieving data from a CSV file and I want to merge all different arrays in one array so that I can perform a sort on it. My array comes like this:
Array ( [0] => kim [1] => abc [2] => xyz )
Array ( [0] => roger [1] => def [2] => ghi )
...
There are more arrays like this and I want to merge all of them in one array and sort it. Currently my code is the following:
<?php
$file = fopen("file.csv","r");
while(! feof($file))
{
$array= fgetcsv($file);
print_r($array);
echo "<br/>";
}
fclose($file);
?>
Please help. Thank You
did you try something like this :
<?php
$file = fopen("file.csv","r");
$array = [];
while(! feof($file))
{
$array = array_merge($array, fgetcsv($file));
}
sort($array);
print_r($array);
fclose($file);
?>

PHP - Fastest way to convert a 2d array into a 3d array that is grouped by a specific value

I would like to convert this two dimensional array of records:
[records] => Array
(
[0] => Array
(
[0] => Pears
[1] => Green
[2] => Box
[3] => 20
)
[1] => Array
(
[0] => Pears
[1] => Yellow
[2] => Packet
[3] => 4
)
[2] => Array
(
[0] => Peaches
[1] => Orange
[2] => Packet
[3] => 4
)
[3] => Array
(
[0] => Apples
[1] => Red
[2] => Box
[3] => 20
)
)
Into this three dimensional array where each array key is grouped by a certain value from the original array:
[converted_records] => Array
(
[Pears] => Array
(
[0] => Array
(
[0] => Green
[1] => Box
[2] => 20
)
[1] => Array
(
[0] => Yellow
[1] => Packet
[2] => 4
)
)
[Peaches] => Array
(
[0] => Array
(
[0] => Orange
[1] => Packet
[2] => 4
)
)
[Apples] => Array
(
[0] => Array
(
[0] => Red
[1] => Box
[2] => 20
)
)
)
I can do this like so:
$array = // Sample data like the first array above
$storage = array();
$cnt = 0;
foreach ($array as $key=>$values) {
$storage[$values[0]][$cnt] = array (
0 => $values[1],
1 => $values[2],
2 => $values[3]
);
$cnt ++;
}
I wanted to know if there is a more optimal way to do this. I am not aware of any functions within PHP that are capable of this so I can only assume that this is basically how it would be done.
The problem is though, this is going to be repeated so so many times and every little millisecond is going to count so I really want to know what is the best way to accomplish this task?
EDIT
The records array is created by parsing a .CSV file as follows:
$records = array_map('str_getcsv', file('file.csv'));
EDIT #2
I did a simple benchmark test on a set of 10 results (5k records each) to get an average runtime of 0.645478 seconds. Granted there is a few other things going on before this so this is not a true indication of actual performance but a good indication for comparison to other methods.
EDIT #3
I did a test with about 20x the records. The average of my routine was 14.91971.
At some point the answer below by #num8er had $records[$key][] = array_shift($data); before updating the answer as it is now.
When I tried testing with the larger set of results this it ran out of memory as its generating an error for each record.
This being said, once i did $records[$key][] = $data; the routine completed with an average of 18.03699 seconds with gc_collect_cycles() commented out.
I've reached the conclusion that although #num8ers method is faster for smaller files, for larger ones my method works out quicker.
If you're only looking for some clean code:
$array = array_map('str_getcsv', file('file.csv'));
$storage = array();
foreach ($array as $values) {
$key = array_shift($values);
$storage[$key][] = $values;
}
Unless you have hundreds of thousands of array entries, speed shouldnt be a concern either.
reading big file to memory using file() (1st iteration when it reads file)
and then iterating lines using array_map (2nd iteration after each line of file is read to array)
doing foreach on array (3rd iteration)
it is bad idea when You're looking for performance.
You're iterating 3 times. so what about 100K records? it will iterate 300K times?
most performant way is to do it while reading file. there is only 1 iteration - reading lines (100K records == 100K iteration):
ini_set('memory_limit', '1024M');
set_time_limit(0);
$file = 'file.csv';
$file = fopen($file, 'r');
$records = array();
while($data = fgetcsv($file)) {
$key = $data[0];
if(!isset($records[$key])) {
$records[$key] = array();
}
$records[$key][] = array(0 => $data[1],
1 => $data[2],
2 => $data[3]);
gc_collect_cycles();
}
fclose($file);
and here is parent -> children processing for huge files:
<?php
ini_set('memory_limit', '1024M');
set_time_limit(0);
function child_main($file)
{
$my_pid = getmypid();
print "Starting child pid: $my_pid\n";
/**
* OUR ROUTINE
*/
$file = fopen($file, 'r');
$records = array();
while($data = fgetcsv($file)) {
$key = $data[0];
if(!isset($records[$key])) {
$records[$key] = array();
}
$records[$key][] = array(0 => $data[1],
1 => $data[2],
2 => $data[3]);
gc_collect_cycles();
}
fclose($file);
unlink($file);
return 1;
}
$file = __DIR__."/file.csv";
$files = glob(__DIR__.'/part_*');
if(sizeof($files)==0) {
exec('split -l 1000 '.$file.' part_');
$files = glob(__DIR__.'/part_*');
}
$children = array();
foreach($files AS $file) {
if(($pid = pcntl_fork()) == 0) {
exit(child_main($file));
}
else {
$children[] = $pid;
}
}
foreach($children as $pid) {
$pid = pcntl_wait($status);
if(pcntl_wifexited($status)) {
$code = pcntl_wexitstatus($status);
print "pid $pid returned exit code: $code\n";
}
else {
print "$pid was unnaturally terminated\n";
}
}
?>

PHP, Building ID'd array from non-incremental ID'd CSV

Basically, here is my CSV File:
1,"Gold"
2,"English Version"
10,"Sword+0"
11,"Sword+1"
12,"Sword+2"
And so on, you get the idea. There are other parts where the ID is not incremental, perhaps one is 2899 and then the next one is 3020. I'm trying to build an array from this with fgetcsv();. I can do it fine, but I've failed so far to match up my array IDs with the ID from the CSV.
Here's a simple one that simply builds an incremental array from the file:
$file = fopen("item_proto.csv", "r");
$i = 1;
while(! feof($file)){
$gvar['item'][$i] = (fgetcsv($file));
$i++;
}
fclose($file);
This of course results in:
Array
(
[item] => Array
(
[1] => Array
(
[0] => 1
[1] => Gold
)
[2] => Array
(
[0] => 2
[1] => English Version
)
[3] => Array
(
[0] => 10
[1] => Sword+0
But I'd like [item][x] to match up with [item][x][y].
Try this:
$file = fopen("item_proto.csv", "r");
$i = 1;
while(! feof($file)){
$line = fgetcsv($file);
$gvar['item'][$line[0]] = $line;
$i++;
}
fclose($file);

Categories