php array_merge without erasing values? - php

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.

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 +...

array_replace_recursive($array, $array2);
This is the solution.

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..

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;

If you also want to keep the values that are blank in both arrays:
array_filter($foo) + array_filter($bar) + $foo + $bar

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));
?>

Related

PHP rename key in Associative array

I've been searching through the articles on SO for this question and tried many of the solutions in my own code but it's not seeming to work.
I have the following array
$array[] = array("Order_no"=>$order_id,
"Customer"=>$customer_id,
"Product"=>$code_po,
"Product_description"=>$code_description,
"Position"=>$pos,
"Qty"=>$item_qty
);
I am looking to replace the "Order_no" key with a variable from a database query, lets assume in this case the variable is "new_name"
$new = "new_name";
$array[$new]=$array["Order_no"];
unset($array["Order_no"]);
print_r($array);
in the print_r statement I am getting the new_name coming through as the correct order number, but I am still seeing "Order_no" there also, which I shouldn't be seeing anymore.
Thanks.
This is your array:
Array
(
[0] => Array
(
[Customer] => 2
[Product] => 99
[Order_no] => 12345
)
)
One way to do it:
<?php
$arr[] = [
"Order_no" => 12345,
"Customer" => 00002,
"Product"=> 99
];
$i_arr = $arr[0];
$i_arr["new_name"] = $i_arr["Order_no"];
unset($i_arr["Order_no"]);
$arr[0] = $i_arr;
print_r($arr);
Another way:
<?php
$arr[] = [
"Order_no" => 12345,
"Customer" => 00002,
"Product"=> 99
];
$arr[0]["new_name"] = $arr[0]["Order_no"];
unset($arr[0]["Order_no"]);
print_r($arr);
To flatten your array out at any time:
<?php
$arr = $arr[0];
print_r($arr);
You are using extra level of array (by doing $array[] = ...).
You should do it with [0] as first index as:
$array[0][$new]=$array[0]["Order_no"];
unset($array[0]["Order_no"]);
Live example: 3v4l
Another option is get ride of this extra level and init the array as:
$array = array("Order_no"=>$order_id, ...
As $array is also an array, you have to use index:
$array[0][$new]=$array[0]["Order_no"];
unset($array[0]["Order_no"]);
The other answers will work for the first time you add to the array, but they will always work on the first item in the array. Once you add another it will not work, so get the current key:
$array[key($array)][$new] = $array[key($array)]["Order_no"];
unset($array[key($array)]["Order_no"]);
If you want the first one, then call reset($array); first.
Change your variable to
$array=array("Order_no"=>$order_id,"Customer"=>$customer_id,"Product"=>$code_po,"Product_description"=>$code_description,"Position"=>$pos,"Qty"=>$item_qty);
or change your code to
$new = "new_name";
$array[0][$new]=$array[0]["Order_no"];
unset($array["Order_no"]);
print_r($array);
Just be careful this would change the order of the array

How to get values of array 1 and array 2 based on array index of array3 in laravel/php? [duplicate]

I wrote this function to get a subset of an array. Does php have a built in function for this. I can't find one in the docs. Seems like a waste if I'm reinventing the wheel.
function array_subset($array, $keys) {
$result = array();
foreach($keys as $key){
$result[$key] = $array[$key];
}
return $result;
}
I always want this too. Like a PHP version of Underscore's pick.
It's ugly and counter-intuitive, but what I sometimes do is this (I think this may be what prodigitalson was getting at):
$a = ['foo'=>'bar', 'zam'=>'baz', 'zoo'=>'doo'];
// Extract foo and zoo but not zam
print_r(array_intersect_key($a, array_flip(['foo', 'zoo'])));
/*
Array
(
[foo] => bar
[zoo] => doo
)
*/
array_intersect_key returns all the elements of the first argument whose keys are present in the 2nd argument (and all subsequent arguments, if any). But, since it compares keys to keys, I use array_flip for convenience. I could also have just used ['foo' => null, 'zoo' => null] but that's even uglier.
array_diff_key and array_intersect_key are probably what you want.
There is no direct function I think in PHP to get a subset from an array1 with compare to another array2 where the values are the list of key name which we fetch.
Like: array_only($array1, 'field1','field2');
But this way can be achieved the same.
<?php
$associative_array = ['firstname' => 'John', 'lastname' => 'Smith', 'DOB' => '2000-10-10', 'country' => 'Ireland' ];
$subset = array_intersect_key( $associative_array, array_flip( [ 'lastname', 'country' ] ) );
print_r( $subset );
// Outputs...
// Array ( [lastname] => Smith [country] => Ireland );

PHP pass by reference issue - can't change type?

I have a weird phenomenon. I hope someone can explain to me what is happening there:
I want to create a filter. The origin is something like '-10' or '10-20' or '20+' (type string) and the result should be 'Under $10', ... as well as 'product_price < 10', ... for a sql command.
But storing the array back on the original string doesn't work. It just delivers '$Array' as result. Is it not possible to pass by reference and change the type?
Thanks for your knowledge!
foreach($filters as &$filter){
preg_match ('#^\-(\d+)$#ism', $filter, $match);
if ($match[1]){
$filter = array(
'Under $'.intval($match[1]),
'product_price < '.intval($match[1])
);
}
...
}
return $filtering;
}
P.S.: I am not looking for a solution, because I could change the origin string into array, or I could change the foreach in to a pass by value and create a new array with the arrays like $newFilter[] = ... I am only curious
You can change it's type. Proof by construction:
<?php
header('Content-type:text/plain');
$arr = array(
'1',
'2',
);
foreach ($arr as &$filter) {
$filter = array($filter);
}
print_r($arr);
?>
Prints:
Array
(
[0] => Array
(
[0] => 1
)
[1] => Array
(
[0] => 2
)
)
You should change your foreach into
foreach($filters as $index => $filter)
and update your filter by doing
$filters[$index] = array(...);
I believe the $filter variable created by the foreach() statement is a copy of the data in the array and not a reference to it.

Get a subset of an array based on an array of keys

I wrote this function to get a subset of an array. Does php have a built in function for this. I can't find one in the docs. Seems like a waste if I'm reinventing the wheel.
function array_subset($array, $keys) {
$result = array();
foreach($keys as $key){
$result[$key] = $array[$key];
}
return $result;
}
I always want this too. Like a PHP version of Underscore's pick.
It's ugly and counter-intuitive, but what I sometimes do is this (I think this may be what prodigitalson was getting at):
$a = ['foo'=>'bar', 'zam'=>'baz', 'zoo'=>'doo'];
// Extract foo and zoo but not zam
print_r(array_intersect_key($a, array_flip(['foo', 'zoo'])));
/*
Array
(
[foo] => bar
[zoo] => doo
)
*/
array_intersect_key returns all the elements of the first argument whose keys are present in the 2nd argument (and all subsequent arguments, if any). But, since it compares keys to keys, I use array_flip for convenience. I could also have just used ['foo' => null, 'zoo' => null] but that's even uglier.
array_diff_key and array_intersect_key are probably what you want.
There is no direct function I think in PHP to get a subset from an array1 with compare to another array2 where the values are the list of key name which we fetch.
Like: array_only($array1, 'field1','field2');
But this way can be achieved the same.
<?php
$associative_array = ['firstname' => 'John', 'lastname' => 'Smith', 'DOB' => '2000-10-10', 'country' => 'Ireland' ];
$subset = array_intersect_key( $associative_array, array_flip( [ 'lastname', 'country' ] ) );
print_r( $subset );
// Outputs...
// Array ( [lastname] => Smith [country] => Ireland );

Add 2 values to 1 key in a PHP array

I have a result set of data that I want to write to an array in php. Here is my sample data:
**Name** **Abbrev**
Mike M
Tom T
Jim J
Using that data, I want to create an array in php that is of the following:
1|Mike|M
2|Tom|T
3|Jim|j
I tried array_push($values, 'name', 'abbreviation') [pseudo code], which gave me the following:
1|Mike
2|M
3|Tom
4|T
5|Jim
6|J
I need to do a look up against this array to get the same key value, if I look up "Mike" or "M".
What is the best way to write my result set into an array as set above where name and abbreviation share the same key?
PHP's not my top language, but try these:
array_push($values, array("Mike", "M"))
array_push($values, array("Tom", "T"))
array_push($values, array("Jim", "J"))
$name1 = $values[1][0]
$abbrev1 = $values[1][1]
or:
array_push($values, array("name" => "Mike", "abbrev" => "M"))
array_push($values, array("name" => "Tom", "abbrev" => "T"))
array_push($values, array("name" => "Jim", "abbrev" => "J"))
$name1 = $values[1]["name"]
$abbrev1 = $values[1]["abbrev"]
The trick is to use a nested array to pair the names and abbreviations in each entry.
$person = array('name' => 'Mike', 'initial' => 'M');
array_push($people, $person);
That said, I'm not sure why you're storing the data separately. The initial can be fetched directly from the name via substr($name, 0, 1).
You will need to create a two dimensional array to store more than one value.
Each row in your result set is already an array, so it will need to be added to your variable as an array.
array_push($values, array('name', 'abbreviation'));
maybe you create a simple class for that as the abbreviation is redundant information in your case
class Person
{
public $name;
pulbic function __construct($name)
{
$this->name = (string)$name;
}
public function getAbbrev()
{
return substr($this->name, 0, 1);
}
public function __get($prop)
{
if ($prop == 'abbrev') {
return $this->getAbbrev();
}
}
}
$persons = array(
new Person('Mike'),
new Person('Tom'),
new Person('Jim')
);
foreach ($persons as $person) {
echo "$person->name ($person->abbrev.)<br/>";
}
You could use two separate arrays, maybe like:
$values_names = array();
$values_initials = array();
array_push($values_names, 'Mike');
array_push($values_initials, 'M');
array_push($values_names, 'Tom');
array_push($values_initials, 'T');
array_push($values_names, 'Jim');
array_push($values_initials, 'J');
So you use two arrays, one for each of the second and third columns using the values in the first one as keys for both arrays.
php arrays work like hash lookup tables, so in order to achieve the desired result, you can initialize 2 keys, one with the actual value and the other one with a reference pointing to the first. For instance you could do:
$a = array('m' => 'value');
$a['mike'] = &$a['m']; //notice the end to pass by reference
if you try:
$a = array('m' => 'value');
$a['mike'] = &$a['m'];
print_r($a);
$a['m'] = 'new_value';
print_r($a);
$a['mike'] = 'new_value_2';
print_r($a);
the output will be:
Array
(
[m] => value
[mike] => value
)
Array
(
[m] => new_value
[mike] => new_value
)
Array
(
[m] => new_value_2
[mike] => new_value_2
)
have to set the same value to both Mike and M for keys.

Categories