ron
ron

Reputation: 89

Combining array string values and discarding array elements

I've got the below $test array sorted by 'date' and 'site'.

$test = array ( 
0 => array ( 
       'id' => '45', 
       'rating' => 'p1', 
       'site' => 'Heavener SW', 
       'time' => '11-03-2012 Sat 10:00:00', 
       ), 
1 => array ( 
       'id' => '45', 
       'rating' => 'h3', 
       'site' => 'Heavener SW', 
       'time' => '11-03-2012 Sat 10:00:00', 
       ),
2 => array ( 
       'id' => '45', 
       'rating' => 'h4', 
       'site' => 'Heavener SW', 
       'time' => '11-03-2012 Sat 16:00:00', 
       ),
3 => array ( 
       'id' => '110', 
       'rating' => 'p3', 
       'site' => 'Red Oak', 
       'time' => '11-03-2012 Sat 16:00:00', 
       ), 
4 => array ( 
       'id' => '110', 
       'rating' => 'h3', 
       'site' => 'Red Oak', 
       'time' => '11-03-2012 Sat 16:00:00', 
       ), 
5 => array ( 
       'id' => '32', 
       'rating' => 'p2', 
       'site' => 'Panopoint', 
       'time' => '11-04-2012 Sun 10:00:00', 
       ) 
 );

I've gone nuts trying to combine 'ratings' for each 'site'-'time' combination.

$result = array (
0 => array ( 
       'id' => '45', 
       'rating' => 'p1, h3', 
       'site' => 'Heavener SW',
       'time' => '11-03-2012 Sat 10:00:00', 
       ),
2 => array ( 
       'id' => '45', 
       'rating' => 'h4', 
       'site' => 'Heavener SW', 
       'time' => '11-03-2012 Sat 16:00:00', 
       ),
3 => array ( 
       'id' => '110', 
       'rating' => 'p3, h3', 
       'site' => 'Red Oak', 
       'time' => '11-03-2012 Sat 16:00:00', 
       ),
5 => array ( 
       'id' => '32', 
       'rating' => 'p2', 
       'site' => 'Panopoint', 
       'time' => '11-04-2012 Sun 10:00:00', 
       ) 
 );

There's no limits on the number of 'ratings' a site can have or how many 'sites' or 'time' there are (within reason). Preserving the index in the $result is optional (ie. not necessary).

I found this similar post(and others) but I don't understand how to implement. Getting all the related ones based on value

I've tried so many differnt ways my head is spinning, any help is greatly appreciated!

Upvotes: 3

Views: 74

Answers (3)

Michael Berkowski
Michael Berkowski

Reputation: 270697

Since preserving the keys is optional, an easy option is to build a new array keyed by the unique pair site|time while looping over the original.

For each of those, we'll also create an array of rating values, and finally, implode() that array into a string back to the 'ratings' key.

$rekeyed = array();
foreach ($test as $item) {
  // Build temporary key by concatenating site, time
  $key = $item['site'] . '|' . $item['time'];
  // If it already exists, add to the ratings
  if (isset($rekeyed[$key])) {
    $rekeyed[$key]['ratings'][] = $item['rating'];
  }
  else {
    // Otherwise, set the key and the ratings array
    $rekeyed[$key] = $item;
    // And set the first rating, initialized as an array
    $rekeyed[$key]['ratings'] = array();
    $rekeyed[$key]['ratings'][] = $item['rating'];
  }
}

// Finally, loop over and implode all the ratings back into a comma-separated string
foreach ($rekeyed as &$item) {
  $item['rating'] = implode(',', $item['ratings']);
  // Don't need the 'ratings' array anymore, unset it
  unset($item['ratings']);
}

var_dump($rekeyed);

Outputs:

array(4) {
  ["Heavener SW|11-03-2012 Sat 10:00:00"]=>
  array(5) {
    ["id"]=>
    string(2) "45"
    ["rating"]=>
    string(5) "p1,h3"
    ["site"]=>
    string(11) "Heavener SW"
    ["time"]=>
    string(23) "11-03-2012 Sat 10:00:00"
  }
  ["Heavener SW|11-03-2012 Sat 16:00:00"]=>
  array(5) {
    ["id"]=>
    string(2) "45"
    ["rating"]=>
    string(2) "h4"
    ["site"]=>
    string(11) "Heavener SW"
    ["time"]=>
    string(23) "11-03-2012 Sat 16:00:00"
  }
  ["Red Oak|11-03-2012 Sat 16:00:00"]=>
  array(5) {
    ["id"]=>
    string(3) "110"
    ["rating"]=>
    string(5) "p3,h3"
    ["site"]=>
    string(7) "Red Oak"
    ["time"]=>
    string(23) "11-03-2012 Sat 16:00:00"
  }
  ["Panopoint|11-04-2012 Sun 10:00:00"]=>
  &array(5) {
    ["id"]=>
    string(2) "32"
    ["rating"]=>
    string(2) "p2"
    ["site"]=>
    string(9) "Panopoint"
    ["time"]=>
    string(23) "11-04-2012 Sun 10:00:00"
  }
}

Upvotes: 1

Baba
Baba

Reputation: 95141

You can simply use array_reduce

$test = array_reduce($test, function ($a, $b) {
    isset($a[$b['time'] . "|" . $b['id']]) ? $a[$b['time'] . "|" . $b['id']]['rating'] .= "," . $b['rating'] : $a[$b['time'] . "|" . $b['id']] = $b;
    return $a;
});

$test = array_values($test);
var_dump($test);

Output

array
  0 => 
    array
      'id' => string '45' (length=2)
      'rating' => string 'p1,h3' (length=5)
      'site' => string 'Heavener SW' (length=11)
      'time' => string '11-03-2012 Sat 10:00:00' (length=23)
  1 => 
    array
      'id' => string '45' (length=2)
      'rating' => string 'h4' (length=2)
      'site' => string 'Heavener SW' (length=11)
      'time' => string '11-03-2012 Sat 16:00:00' (length=23)
  2 => 
    array
      'id' => string '110' (length=3)
      'rating' => string 'p3,h3' (length=5)
      'site' => string 'Red Oak' (length=7)
      'time' => string '11-03-2012 Sat 16:00:00' (length=23)
  3 => 
    array
      'id' => string '32' (length=2)
      'rating' => string 'p2' (length=2)
      'site' => string 'Panopoint' (length=9)
      'time' => string '11-04-2012 Sun 10:00:00' (length=23)

See Live Demo

Upvotes: 2

Steven Liao
Steven Liao

Reputation: 3787

I think PHP foreach loops go in order, so this should work. I haven't tested it, so let me know how it goes.

$previous_id = '';
// for each row, check if it was the same as the previous row (since you have sorted it properly)
foreach($result as $k => $row){
    // if it matches the previous, add the rating and remove now duplicate row
    if ($row['time'] == $result[$previous_id]['time']
     && $row['site'] == $result[$previous_id]['site']){
        $result[$previous_id]['rating'] .= ', '.$row['rating'];
        unset($result[$k]);
    } // otherwise, set new previous_id to check
    else {
        $previous_id = $k;
    }
}

This has the added benefit of keeping the first row-id that you come across, but may not be particularly optimized.

Upvotes: 0

Related Questions