views:

90

answers:

2

As I was writing a for loop earlier today, I thought that there must be a neater way of doing this... so I figured I'd ask. I looked briefly for a duplicate question but didn't see anything obvious.

The Problem:

Given N arrays of length M, turn them into a M-row by N-column 2D array

Example:

$id   = [1,5,2,8,6]
$name = [a,b,c,d,e]
$result = [[1,a],
           [5,b],
           [2,c],
           [8,d],
           [6,e]]

My Solution:

Pretty straight forward and probably not optimal, but it does work:

<?php
// $row is returned from a DB query
// $row['<var>'] is a comma separated string of values
$categories = array();
$ids = explode(",", $row['ids']);
$names = explode(",", $row['names']);
$titles = explode(",", $row['titles']);
for($i = 0; $i < count($ids); $i++) {
    $categories[] = array("id" => $ids[$i],
                          "name" => $names[$i],
                          "title" => $titles[$i]);
}
?>

note: I didn't put the name => value bit in the spec, but it'd be awesome if there was some way to keep that as well.

+1  A: 

Yup, array_combine()

$result = array_combine( $id, $name );

EDIT

Here's how I'd handle your data transformation

function convertRow( $row )
{
  $length = null;
  $keys = array();

  foreach ( $row as $key => $value )
  {
    $row[$key] = explode( ',', $value );
    if ( !is_null( $length ) && $length != count( $row[$key] ) )
    {
      throw new Exception( 'Lengths don not match' );
    }
    $length = count( $row[$key] );

    // Cheap way to produce a singular - might break on some words
    $keys[$key] = preg_replace( "/s$/", '', $key );
  }

  $output = array();

  for ( $i = 0; $i < $length; $i++ )
  {
    foreach ( $keys as $key => $singular )
    {
      $output[$i][$singular] = $row[$key][$i];
    }
  }

  return $output;
}

And a test

$row = convertRow( $row );
echo '<pre>';
print_r( $row );
Peter Bailey
won't this result in $result = [1=>'a', 5=>'b', etc.] which is slightly different than the request, though perhaps what is actually desired.
sprugman
That's *exactly* what it will result in, which is what his first code snippet shows. I asked him for clarification - still waiting on that.
Peter Bailey
Which code snippet? The first is just a data set of 3 arrays. The second shows the resulting array to resemble something like `$result[0] = array(1, a, array(1, a));` -- __e:__ nvm, I see now. I was reading result as title. Even still I think it's displaying the id as the first member of the array, and the name as the second. Not as the id being the key.
anomareh
array_combine gives key=>value pairs, where I'm looking to create an array of arrays. perhaps I used ambiguous data in my example, but I'm looking for [1,a], [5,b], etc. to be arrays (so if a third array was added it would end up being [1,a,x], [5,b,y], etc.)
Ty W
Expanded my answer to better fit what you're after
Peter Bailey
+3  A: 

Maybe this? Not sure if it's more efficient but it's definitely cleaner.

/*
Using the below data:

$row['ids'] = '1,2,3';
$row['names'] = 'a,b,c';
$row['titles'] = 'title1,title2,title3';
*/

$categories = array_map(NULL,
  explode(',', $row['ids']),
  explode(',', $row['names']),
  explode(',', $row['titles'])
);

// If you must retain keys then use instead:
$withKeys = array();

foreach ($row as $k => $v) {
    $v = explode(',', $v);

    foreach ($v as $k2 => $v2) {
        $withKeys[$k2][$k] = $v[$k2];
    }
}

print_r($categories);
print_r($withKeys);

/*
$categories:

array
  0 => 
    array
      0 => int 1
      1 => string 'a' (length=1)
      2 => string 'title1' (length=6)
...

$withKeys:

array
  0 =>
    array
      'ids' => int 1
      'names' => string 'a' (length=1)
      'titles' => string 'title1' (length=6)
...
*/

Just did a quick simple benchmark for the 4 results on this page and got the following:

// Using the following data set:
$row = array(
    'ids'       =>  '1,2,3,4,5',
    'names'     =>  'abc,def,ghi,jkl,mno',
    'titles'    =>  'pqrs,tuvw,xyzA,BCDE,FGHI'
);

/*
For 10,000 iterations,

Merge, for:
0.52803611755371

Merge, func:
0.94854116439819

Merge, array_map:
0.30260396003723

Merge, foreach:
0.40261697769165
*/
anomareh
Just a quibble, but you could also ditch $k to reduce the number of variables. "$withKeys[$k2][] = $v[$k2];" would work just as well. Would that affect performance? Danged if I know.
abeger
very nice, thanks for the benchmarks :D
Ty W
@abeger if you make that change, keys won't be carried over which is the only point of the nested `foreach` loops. If you don't want keys to carry over just use `array_map()` with the `NULL` callback as it's faster.
anomareh
@anomareh Oh, whoops. I misread your example. Sorry for the inaccurate quibble.
abeger