15

Background: Trevor is working with a PHP implementation of a standard algorithm: take a main set of default name-value pairs, and update those name-value pairs, but only for those name-value pairs where a valid update value actually exists.

Problem: by default, PHP array_merge works like this ... it will overwrite a non-blank value with a blank value.

$aamain   =   Array('firstname'=>'peter','age'=>'32','nation'=>'');
$update   =   Array('firstname' => '','lastname' => 'griffin', age =>'33','nation'=>'usa');

print_r(array_merge($aamain,$update));    
/*
Array
(
    [firstname] =>           // <-- update set this to blank, NOT COOL!
    [age] => 33              // <-- update set this to 33, thats cool
    [lastname] => griffin    // <-- update added this key-value pair, thats cool
    [nation] => usa          // <-- update filled in a blank, thats cool.
)
*/

Question: What's the fewest-lines-of-code way to do array_merge where blank values never overwrite already-existing values?

print_r(array_coolmerge($aamain,$update));    
/*
Array
(
    [firstname] => peter  // <-- don't blank out a value if one already exists!
    [age] => 33
    [lastname] => griffin
    [nation] => usa

)
*/

UPDATE: 2016-06-17T11:51:54 the question was updated with clarifying context and rename of variables.

dreftymac
  • 31,404
  • 26
  • 119
  • 182
  • 1
    What do you want to happen with the duplicates? – elzapp Apr 27 '09 at 13:26
  • the standard behavior for handling duplicates is fine: the key-value pair in feebar will overwrite the corresponding key-value pair in foobar. The *only* difference I want is to never let feebar blank out a value in foobar. – dreftymac Apr 27 '09 at 13:40
  • **See also** (python-based) http://stackoverflow.com/questions/38987/how-to-merge-two-python-dictionaries-in-a-single-expression/39858#39858 – dreftymac Dec 28 '16 at 19:26
  • **See also** (python-based) http://stackoverflow.com/q/6354436/42223 – dreftymac Jan 01 '17 at 21:23
  • **See also** (php zeroes) https://stackoverflow.com/questions/14134006/remove-null-false-and-but-not-0-from-a-php-array#14134055 – dreftymac Apr 18 '18 at 15:30

6 Answers6

16

Well, if you want a "clever" way to do it, here it is, but it may not be as readable as simply doing a loop.

$merged = array_merge(array_filter($foo, 'strval'), array_filter($bar, 'strval'));

edit: or using +...

Matt
  • 2,229
  • 12
  • 10
  • Thanks for that one. Avoiding a loop can be seen as a kind of "useless cleverness" on some level perhaps -- but it's just nice to not have to *always* use loops when something like map, select, or a list comprehension (as implemented in some other languages) seems more concise. – dreftymac Apr 27 '09 at 14:20
  • 3
    Using 'trim' instead of 'strval' will also clear out items which only contain whitespace. – too much php Sep 29 '09 at 09:28
5
array_replace_recursive($array, $array2);

This is the solution.

timo
  • 2,119
  • 1
  • 24
  • 40
  • 4
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Jonas Czech Mar 14 '16 at 20:02
  • 3
    This is by far the best answer here. Thanks! – tmarois Aug 03 '17 at 03:26
  • 1
    I agree, this is the best solution here. No explanation needed. PHP documentation is sufficient. – GRowing Aug 03 '17 at 17:08
  • 1
    Am I missing something or isn't this at all what OP asked? OP's result using this answer would be: { ["firstname"] => "", ["age"] => "33", ["nation"] => "usa", ["lastname"] => "griffin" } – Tum Dec 13 '22 at 19:17
5

Try this:

$merged = array_map(
    create_function('$foo,$bar','return ($bar?$bar:$foo);'),
    $foobar,$feebar
);

Not the most readable solution, but it should replace only non-empty values, regardless of which order the arrays are passed..

Calvin
  • 4,559
  • 1
  • 25
  • 23
4

Adjust to your needs:

# Replace keys in $foo
foreach ($foo as $key => $value) {
    if ($value != '' || !isset($bar[$key])) continue;
    $foo[$key] = $bar[$key];
}

# Add other keys in $bar
# Will not overwrite existing keys in $foo
$foo += $bar;
soulmerge
  • 73,842
  • 19
  • 118
  • 155
2

If you also want to keep the values that are blank in both arrays:

array_filter($foo) + array_filter($bar) + $foo + $bar
Russ
  • 3,768
  • 7
  • 28
  • 37
1

This will put duplicates into a new array, I don't know if this is what you want though.

<?php
  $foobar =   Array('firstname' => 'peter','age' => '33',);
  $feebar =   Array('firstname' => '','lastname' => 'griffin',);
  $merged=$foobar;
  foreach($feebar as $k=>$v){
    if(isset($foobar[$k]))$merged[$k]=array($v,$foobar[$k]);
    else $merged[$k]=$v;
  }
  print_r($merged);
?>

This will simply assure that feebar will never blank out a value in foobar:

<?php
  $foobar =   Array('firstname' => 'peter','age' => '33',);
  $feebar =   Array('firstname' => '','lastname' => 'griffin',);
  $merged=$foobar;
  foreach($feebar as $k=>$v) if($v)$merged[$k]=$v;
  print_r($merged);
?>

or ofcourse,

<?
  function cool_merge($array1,$array2){
    $result=$array1;
    foreach($array2 as $k=>$v) if($v)$result[$k]=$v;
    return $result;
  }

  $foobar =   Array('firstname' => 'peter','age' => '33',);
  $feebar =   Array('firstname' => '','lastname' => 'griffin',);
  print_r(cool_merge($foobar,$feebar));
?>
elzapp
  • 1,961
  • 4
  • 15
  • 22