How to parse key/value chain into associative array in PHP? - php

I have a string that looks like: key1/value1/key2/value2/ and so on
I did an explode('/',trim($mystring,'/')) on that
Now I want to haven an associative array like:
array(
'key1' => 'value1',
'key2' => 'value2',
...
);
of course i can do it with an for loop where i always read two entries and push them into a target array, but isnt there something more efficient and elegant? a one liner with some some php core function similar to ·list()· or something?

A method using array_walk() with a callback function:
function buildNewArray($value, $key, &$newArray) {
(($key % 2) == 0) ? $newArray[$value] = '' : $newArray[end(array_keys($newArray))] = $value;
}
$myString = 'key1/value1/key2/value2/';
$myArray = explode('/',trim($myString,'/'));
$newArray = array();
array_walk($myArray, 'buildNewArray', &$newArray);
var_dump($newArray);

If the format is not changin (i mean it is allways Key1/value1/key2/value2) it should be easy.
After the explode you have this:
$arr = Array('key1','value1','key2','value2')
so you now can do:
$new_arr = array();
$i = 0;
for($i=0;$i<count($arr);$i+=2)
{
$new_arr[$arr[i]] = $arr[$i+1];
}
at the end of the loop you will have:
$new_arr = array("key1"=>"value1","key2"=>"value2")
KISS :)
HTH!

One can always make yourself a one liner.
This is called User-defined functions
No need to devise something ugly-but-one-liner-at-any-cost.
Make your own function and you will get much shorter, cleaner and way more reliable result.
A function, which will not only do elementary string operations but also do something more intelligent, like parameter validation.

Related

Array Map? Walk? Something Else?

Thanks for all the inputs on the last two questions I asked. Slowly but surely, gettin there in terms of learning more. Now this next question has to do with array mapping/walking. I am not sure how to get this to work, but this is what's going on:
I have three arrays, each made up of keys with their own arrays. Each array looks like:
$arr1 = array(
'service3'=>array('price'=>'2.00','transit'=>'3'),
'service2'=>array('price'=>'4.00', 'transit'=>'1')
);
$arr2 = array(
'service1'=>array('price'=>'6.00','transit'=>'2'),
'service2'=>array('price'=>'8.00', 'transit'=>'1')
);
arr3 = array(
'service3'=>array('price'=>'2.00','transit'=>'3'),
'service2'=>array('price'=>'4.00', 'transit'=>'1')
);
and so on.
What I want to do is add the services from each of the three arrays with the matching keys. The caveats are that each array may be of a different length, and each may not have the same service (see above array 3 for what I mean).
I was looking for a cleaner way to add the three arrays together without running a bunch of foreach loops or creating temp arrays. It seems like array_map with a callback function would be a good way to go, or perhaps even array_walk. Just not sure how to proceed, because of the fact that the arrays may not be of the same length or even have the same service keys.
Here is a quick function, you can use (untested):
<?php
function group_by_service() {
$return = array();
$arrays = func_get_args();
foreach($arrays as $arr) {
foreach($arr as $service => $props) {
if (!array_key_exists($service, $return)) {
$return[$service] = $props;
} else {
$return[$service]['price'] += $props['price'];
$return[$service]['transit'] += $props['transit'];
}
}
}
return $return;
}
You can, then, use this function like this:
<?php
$arr1 = array(
'service3'=>array('price'=>'2.00','transit'=>'3'),
'service2'=>array('price'=>'4.00', 'transit'=>'1')
);
$arr2 = array(
'service1'=>array('price'=>'6.00','transit'=>'2'),
'service2'=>array('price'=>'8.00', 'transit'=>'1')
);
arr3 = array(
'service3'=>array('price'=>'2.00','transit'=>'3'),
'service2'=>array('price'=>'4.00', 'transit'=>'1')
);
$grouped = group_by_service($arr1, $arr2, $arr3);
print_r($grouped);

"Classic" array in php

So, I'm starting a new project and working with php for the first time.
I get that the average definition and functioning of arrays in php is actually pretty much a namevalue combo.
Is there some syntax, API, or other terminology for just a simple list of items?
I.e. inserting something like ['example','example2','example3','example4'] that I can just call based off their index position of the array, without having to go in and modify the syntax to include 0 => 'example', etc...
This is a very shortlived array so im not worried about long term accessibility
php arrays are simple to use. You can insert into an array like:
$array=array('a','b','c'.....);
Or
$array[]="a";
$array[]="b";
$array[]="c";
or
array_push($array, "a");
array_push($array, "b");
array_push($array, "c");
array_push($array, "d");
and call them by their index values:
$array[0];
this will give you a
$yourArray = array('a','b','c');
or
$yourArray[] = 'a';
$yourArray[] = 'b';
$yourArray[] = 'c';
will get you an array with integer index values instead of an associative one..
You still can use array as "classic" arrays in php, just the way you think.
For example :
<?php
$array = array("First", "Second", "Third");
echo $array[1];
?>
You can then add different values <?php $array[] = "Forth"; ?> and it will be indexed in the order you specified it.
Notice that you can still use it as an associative array :
<?php
$array["newValue"] = "Fifth";
$array[1] = "ReplaceTheSecond";
$array[10] = "";
?>
Arrays in PHP can either be based on a key, like 0 or "key" => "value", or values can just be "appended" to the array by using $array[] = 'value'; .
So:
$mine = array();
$mine[] = 'test';
$mine[] = 'test2';
echo $mine[0];
Would produce 'test';
Haven't tested the code.

Ordered array to associative array, odd values as keys

Pretty straightforward:
// turn
array('foo', 'bar', 'hello', 'world');
// into
array('foo' => 'bar', 'hello' => 'world');
Right now I'm using:
do{
$arrayOut[current($arrayIn)] = next($arrayIn);
}while(next($arrayIn));
I'm wondering if there's a way to do it without the intermediary variable, $arrayOut. I could write a function, however this is a single use case, and I'm trying to keep my script uncluttered. I'm just wondering if there's something I missed in the docs that would serve this purpose.
The values are coming from a routing path:
route/to/controller/action/key1/value1/key2/value2
It's exploded, and eventually after using the other components, I'm left with ('key1', 'value1', 'key2', 'value2', ...)
Thank you folks for the insight and suggestions. Long Ears won this one for the concise approach, which when expanded to more than "1 line", is not terribly cryptic (I don't think at least)
However, also with regard to Long Ears' suggestion, perhaps my desire for semantically precise code with minimal verbosity has gotten the better of me, and I was chasing daisies trying to keep my variable scope "pollutant-free", to paraphrase myself.
You can make your existing code slightly more efficient (removes one function call per iteration, for one at the beginning:)
$b = array();
array_unshift($a, false);
while (false !== $key = next($a)) {
$b[$key] = next($a);
}
Ok, (shudder) here's your one liner:
$b = call_user_func_array('array_merge', array_map(function($v) { return array($v[0] => $v[1]); }, array_chunk($a, 2)));
You do not need an array, you can do it directly with the path text:
$path = "foo/bar/hello/world";
preg_match_all("#(?J)(?<key>[^/]+)/(?<val>[^/]*)#", $path, $p);
$params = array_combine($p['key'], $p['val']);
print_r($params);
/*
Array
(
[foo] => bar
[hello] => world
)
*/
I don't think you can do better than what you have. But what about trying to put the odd and even elements in different arrays when your input is read? You could then use array_combine to make an one-liner.
Update: array_map won't help, as it will produce an array with an equal number of elements. You can do things with array_filter and array_combine, but again, that won't end up being shorter.
I really like what you have; short, simple, does the job. I 'd simply refactor it out into a function, let's say array_interleave_combine or some such.
Update 2:
Well, you asked for it, so here it is. Tries to be too clever if you ask me. Is an one-liner, but I really really don't think the "pureness" of this is enough to pay back the lost time someone would need to understand what's going on:
$result = array_reduce(
array('foo', 'bar', 'hello', 'world'),
function($partial, $item) {
static $nextKey;
if ($nextKey === null) {
$nextKey = $item;
}
else {
$partial[$nextKey] = $item;
$nextKey = null;
}
return $partial;
});
This solution does not need an additional array:
$arr = array('foo', 'bar', 'hello', 'world');
for($i = 0, $count = count($arr); $i < $count; $i += 2)
{
$arr[$arr[$i]] = $arr[$i + 1];
unset($arr[$i], $arr[$i + 1]);
}

Populating PHP list() with values in an array

I have an array:
$arr = array('foo', 'bar', 'bash', 'monkey', 'badger');
I want to have the elements in that array appear as the variables in my list():
list($foo, $bar, $bash, $monkey, $badger) = $data;
Without actually specifying the variables, I tried;
list(implode(",$", $arr)) = $data; and
list(extract($arr)) = $data;
But they don't work, I get:
Fatal error: Can't use function return value in write context
Does anyone have any idea whether this is possible?
UPDATE: more context:
I am getting a CSV of data from an API, the first row is column names, each subsequent row is data. I want to build an associative array that looks like this:
$data[0]['colname1'] = 'col1data';
$data[0]['colname2'] = 'col2data';
$data[0]['colname3'] = 'col3data';
$data[1]['colname1'] = 'col1data';
$data[1]['colname2'] = 'col2data';
$data[1]['colname3'] = 'col3data';
Before I do that, however, I want to make sure I have all the columns I need. So, I build an array with the column names I require, run some checks to ensure the CSV does contain all the columns I need. Once thats done, the code looks somewhat like this (which is executed on a foreach() for each row of data in the CSV):
//$data is an array of data WITHOUT string indexes
list( $col1,
$col2,
$col3,
...
$col14
) = $data;
foreach($colNames AS $name)
{
$newData[$i][$name] = $$name;
}
// Increemnt
$i++;
As I already HAVE an array of column name, I though it would save some time to use THAT in the list function, instead of explicitly putting in each variable name.
The data is cleaned and sanitised elsewhere.
Cheers,
Mike
I want to have the elements in that array appear as the variables in my list():
i think there is your problem in understanding. list() does not create a new list structure or variable, it can only be used to assign many variables at once:
$arr = array(1, 2, 3);
list($first, $second, $third) = $arr;
// $first = 1, $second = 2, $third = 3
see http://php.net/list for more information.
you are probably looking for an associative array. you can create it with the following code:
$arr = array('first' => 1, 'second' => 2, 'third' => 3);
// $arr['first'] = 1, …
If some rows in your input file are missing columns, you can't really know which one is missing. Counting the number of values and aborting or jumping to next row when less than expected should be enough.
... unless you set the rule that last columns are optional. I'll elaborate on this.
Your code sample is far for complete but it seems that your problem is that you are using arrays everywhere except when matching column names to cell values. Use arrays as well: you don't need individual variables and they only make it harder. This is one of the possible approaches, not necessarily the best one but (I hope) clear enough:
<?php
$required_columns = array('name', 'age', 'height');
$input_data = array(
array('John', 33, 169),
array('Bill', 40, 180),
array('Ashley', 21, 155),
array('Vincent', 13), // Incomplete record
array('Michael', 55, 182),
);
$output = array();
foreach($input_data as $row_index => $row){
foreach($required_columns as $col_index => $column_name){
if( isset($row[$col_index]) ){
$output[$row_index][$column_name] = $row[$col_index];
}
}
}
print_r($output);
?>
I've used $row_index and $col_index for simplicity.
Original answer, for historical purposes only ;-)
I can't really understand your specs but PHP features variable variables:
<?php
$arr = array('foo', 'bar', 'bash', 'monkey', 'badger');
foreach($arr as $i){
$$i = $i;
}
?>
Now your script has these variables available: $foo, $bar... It's quite useless and potentially dangerous but it does what you seem to need.
You are trying to use a language construct in a manner in which it's not meant to be used. Use variable variables as Alvaro mentioned.
$arr = array('foo', 'bar', 'bash', 'monkey', 'badger');
foreach ($arr as $index => $key) {
$$key = $data[$index];
}
or
$arr = array('foo', 'bar', 'bash', 'monkey', 'badger');
$result = array();
foreach ($arr as $index => $key) {
$result[$key] = $data[$index];
}
extract($result);
In short, do not use "list", use arrays and associated arrays. Write helper functions to make your code clearer.
why not immediatly call the vars like
$arr['foo']
or
$arr[0]
If you want to extract the elements of $data into the current context, you can do that with extract. You might find it useful to call array_intersect_key first to pare down $data to the elements that you want to extract.
May be try like this :
$arr = array('foo', 'bar', 'bash', 'monkey', 'badger');
$data = "$".implode(",", $arr);
echo str_replace(",", ",$", $data);

PHP: Apply a function to multiple variables without using an array

I have a function (for ease, I'll just use count()) that I want to apply to maybe 4-5 different variables. Right now, I am doing this:
$a = count($a);
$b = count($b);
$c = count($c);
$d = count($d);
Is there a better way? I know arrays can use the array_map function, but I want the values to remain as separate values, instead of values inside of an array.
Thanks.
I know you said you don't want the values to be in an array, but how about just creating an array specifically for looping through the values? i.e.:
$arr = Array($a, $b, $c, $d);
foreach ($arr as &$var)
{
$var = count($var);
}
I'm not sure if that really is much tidier than the original way, though.
If you have a bunch of repeating variables to collect data your code is poorly designed and should just be using an array to store the values, instead of dozens of variables. So perhaps you want something like:
$totals = array("Visa"=>0,"Mastercard"=>0,"Discover"=>0,"AmericanExpress"=>0);
then you simply add to your array element (say from a while loop from your SQL or whatever you are doing)
$totals['Visa'] += $row['total'];
But if you really want to go down this route, you could use the tools given to you, if you want to do this with a large batch then an array is a good choice. Then foreach the array and use variable variables, like so:
$variables = array('a','b','c'...);
foreach ( $variables as $var )
{
${$var} = count(${var});
}
What Ben and TravisO said, but use array_walk for a little cleaner code:
$arr = Array($a, $b, $c, $d);
array_walk($arr, count);
You can use extract to get the values back out again.
//test data
$a = range(1, rand(4,9));
$b = range(1, rand(4,9));
$c = range(1, rand(4,9));
//the code
$arr = array('a' => $a, 'b' => $b, 'c' => $c);
$arr = array_map('count', $arr);
extract($arr);
//whats the count now then?
echo "a is $a, b is $b and c is $c.\n";
How do you measure "better"? You might be able to come up with something clever and shorter, but what you have seems like it's easiest to understand, which is job 1. I'd leave it as it is, but assign to new variables (e.g. $sum_a, ...).

Categories