简体   繁体   中英

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:

  1. Each line has 3 fields.
  2. 3rd field becomes a key in a new associative array.
  3. Two remaining fields (1st and 2nd) become values for that key .
  4. 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.

PS 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:

  1. Not reading the whole file in memory. fgetcsv reads one line at a time
  2. 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 :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM