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, ...).
Related
I have two large arrays:
$a = array('a1','a2','a3','a4'); // and so on
$b = array('b1'=>'a1', 'b2'=>'a3', 'b3'=>'a1'); //
I would like to get the following result:
$a = array('a1'=>array('b1', 'b3'), 'a2'=>array(), 'a3'=>array('b2') );
I could just do:
foreach($a as $aa){
foreach($b as $bb){
// check with if then add to a
}
}
but it would get tremendously large with bigger numbers.
So it occurred to me that if i remove each 'b' element after being added to $a the next loops will be smaller, and i would cut on resources.
However when splicing the looped array, the index does not seem to get updated, and the next loop does not take into consideration that fact that it was cut down by 1.
How can I make this work, and also, is there a better way of fitting items of an array into the appropriate indexes of another array?
EDIT:
how would this be done if the structure of both $a and $b were:
$a[0]['Word']['id']=1;
$a[0]['Word']['sentence_id']=2;
$a[0]['Word']['word_string']='someWord';
$b[0]['Word']['id']=3;
$b[0]['Word']['sentence_id']=4;
$b[0]['Word']['word_string']='someWord';
// And i would like to list `b` like so:
$a[0]['list_of_bs']=array(b[0], b[1]);
//So that i can get:
echo $a[0]['list_of_bs']['Word']['id'];
// and to get result 3
and i would like it to be $a[0][Word][list_of_b]=array(b1,b2,b3) and each of the b's has it's own data in associative array.
Try this,
$a = array('a1','a2','a3','a4');
$b = array('b1'=>'a1', 'b2'=>'a3', 'b3'=>'a1');
foreach($a as $values)
{
$key = array_keys($b, $values);
$new_array[$values] = $key;
}
$new_array -> will be the result that you need.
No there is no other way which removes load from the server.
You need to do this in this way only.
Even array_walk also need to perform it like in the loopy way.
The way you put together the two loops is not well thought. Don't put them together but after each other:
foreach ($a as $aa) {
// transform a
}
foreach ($b as $bb){
// check with if then add to a
}
This will make count(a) + count(b) iterations instead of count(a) * count(b) iterations - which is less unless a and b have only one element.
Taking idea from #hakre ..
Why not loop through $b
new_arr = new array
foreach $b as $bb
new_arry($bb->val).push($bb->key)
foreach $new_arry as $nn
if $nn doesn't exist in $a then remove
PHP's list keyword is nice for taking out variables from an array $a as below
$a = array(1,22);
list($b, $c) = $a;
var_dump("$a $b $c");
But for array $a2 in the form of key => value as below, I failed to use list
$a2 = array('b'=>1,'c'=>22);
list($b, $c) = $a2;
list($bkey, $b, $ckey, $c) = $a2;
list( list($bkey, $b), list($ckey,$c) ) = $a2;
var_dump("$a2 $b $c");
All of the three above assignments fail. I give up.
If you know how to get the key & value in array $a2, please help!
Following the comment of Mr Evil below (Col Shrapnel, see his profile), I never said following two ways are different, one could use either but I have advised using these methods on user-inputted data could create security problems, use it on your own risk or if there is no user-inputted data.
It does not seem to work with associatieve arrays, you can do something like this though:
foreach ($array as $key => $value) {
$$key = $value;
}
Example:
$a2 = array('b'=>1,'c'=>22);
foreach ($a2 as $key => $value) {
$$key = $value;
}
echo $b . '<br>';
echo $c;
Result:
1
22
One could also use extract() function but I generally avoid it because using it on user-inputted values could create security hazards. Depending on your choice, you might want to use it or if data isn't coming from users side.
I think you should use each function.
each() Return the current key and value pair from an array and advance the array cursor.
it seems it is extract() function you need
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]);
}
Lets assume we have two PHP-Arrays:
$some_array = array('a','b','c','d','e','f');
$another_array = array(101,102,103,104,105,106);
In PHP, there are already some Array-Functions that allow the construction of an Associative Array (AKA hash), e,g,;
$hash = array_combine(
$some_array,
$another_array
);
And here it comes. What should I do if I want to create a hash in a more functional style, if I want to compute key and value on the fly and build the hash through a map-operation, like (not working):
# wishful thinking
$hash = array_map(
function($a, $b){ return ($a => $b); },
$some_array,
$another_array
);
The problem here seems to be the expectation, the line
...
function($a, $b){ return ($a => $b); }
...
would indeed return an key value/pair of a hash - which it doesn't.
Q: How can I return a key/value pair from a function - which can be used to build up an associative array?
Addendum
To make clear what I really was looking for, I'll provide a perl example of hash generation:
...
# we have one array of characters (on which our hash generation is based)
my #array = qw{ a b c d e f };
# now *generate* a hash with array contents as keys and
# ascii numbers as values in a *single operation *
# (in Perl, the $_ variable represents the actual array element)
my %hash = map +($_ => ord $_), #array;
...
Result (%hash):
a => 97
b => 98
c => 99
d => 100
e => 101
f => 102
From the responses, I'd now think this is impossible in PHP. Thanks to all respondends.
EDIT: It's not entirely clear whether you're having a problem merely with returning multiple variables from a function, or whether you're having problems storing a function in an array. Your post gives the impression that storing the function in the array works, so I'll tackle the return-multiple-variables problem.
There is no way to return a single instance of a key/value pair in PHP. You have to have them in an array... but remember that in PHP, an array and hashmap are exactly the same thing. It's weird (and controversial), but that means it's perfectly legitimate to return an array/hashmap with the multiple values you wish to return.
There are only two sane ways that I know (from 10+ years of PHP experience) to get more than one variable out of a function. One is the good'ol fashion way of making the input variable changeable.
function vacuumPackSandwitch(&$a, &$b) {
$a = 505;
$b = 707;
}
This will change both $a and $b as opposed to changing copies of them like usual. For example:
$a = 1;
$b = 2;
vacuumPackSandwitch($a, $b);
print $a.' '.$b;
This will return "505 707", not "1 2" like normally. You might have to do:
vacuumPackSandwitch(&$a, &$b);
But if that's the case, the PHP compiler will duly let you know.
The other way is to return an array, which I suppose is the clearer and preferred way.
function ($a, $b) {
return array($a, $b);
}
You can grab both variables at the same time by doing:
list($c, $d) = vacuumPackSandwitch($a, $b);
Hope it helps!
In PHP arrays can be associative too! An int-indexed array is just an associative array with 0 => element0 and so on. Thus, you can accomplish what you are looking for like this:
$somearray = array('name' => 'test', 'pass' => '***', 'group' => 'admin');
function myfunc($a, $b) { return array($a => $b); }
if($somearray['name'] == 'test') { echo 'it works!'; }
// Let's add more data with the function now
$somearray += myfunc('location', 'internet');
//Test the result
if($somearray['location'] == 'internet') { echo 'it works again!'; }
It is really very simple. Hope this helps.
I know that this is an old question, but google still likes it for a search I recently made, so I'll post my findings. There are two ways to do this that come close to what you're attempting, both relying on the same general idea.
Idea 1:
Instead of returning a key => value pair, we return an array with only one element, 'key => value', for each sequential element of the original arrays. Then, we reduce these arrays, merging at every step.
$array = array_map(
function($a, $b){
return array($a => $b);
},
$arr1,
$arr2
);
$array = array_reduce(
$array,
function($carry, $element){
$carry = array_merge($carry, $element);
return $carry;
},
array()
);
OR
Idea 2:
Similar to idea one, but we do the key => value assignment in array_reduce. We pass NULL to array_map, which creates an array of arrays (http://php.net/manual/en/function.array-map.php)
$array = array_map(NULL, $a, $b);
$array = array_reduce(
$array,
function($carry, $element){
$carry[$element[0]] = $element[1];
return $carry;
},
array()
);
Personally, I find Idea 2 to be a lot more elegant than Idea 1, though it requires knowing that passing NULL as the function to array_map creates an array of arrays and is therefore somewhat un-intuitive. I just think of it as a precursor to array_reduce, where all the business happens.
Idea 3:
$carry = array();
$uselessArray = array_map(
function($a, $b) use ($carry){
$carry[$a] = $b;
},
$a,
$b
);
Idea 3 is an alternative to Idea 2, but I think it's hackier than Idea 2. We have to use 'use' to jump out of the function's scope, which is pretty ugly and probably contrary to the functional style OP was seeking.
Lets just streamline Idea 2 a little and see how that looks:
Idea 2(b):
$array = array_reduce(
array_map(NULL, $a, $b),
function($carry, $element){
$carry[$element[0]] = $element[1];
return $carry;
},
array()
);
Yeah, that's nice.
How else might you compare two arrays ($A and $B )and reduce matching elements out of the first to prep for the next loop over the array $A?
$A = array(1,2,3,4,5,6,7,8);
$B = array(1,2,3,4);
$C = array_intersect($A,$B); //equals (1,2,3,4)
$A = array_diff($A,$B); //equals (5,6,7,8)
Is this the simplest way or is there a way to use another function that I haven't thought of? My goal is to have an array that I can loop over, pulling out groups of related content (I have defined those relationships elsewhere) until the array returns false.
You've got it. Just use array_diff or array_intersect. Doesn't get much easier than that.
Edit:
For example:
$arr_1 = array_diff($arr_1, $arr_2);
$arr_2 = array_diff($arr_2, $arr_1);
Source
Dear easy and clean way
$clean1 = array_diff($array1, $array2);
$clean2 = array_diff($array2, $array1);
$final_output = array_merge($clean1, $clean2);
See also array_unique. If you concatenate the two arrays, it will then yank all duplicates.
Hey, even better solution: array _ uintersect.
This let's you compare the arrays as per array_intersect but then it lets you compare the data with a callback function.
Try to this
$a = array(0=>'a',1=>'x',2=>'c',3=>'y',4=>'w');
$b = array(1=>'a',6=>'b',2=>'y',3=>'z');
$c = array_intersect($a, $b);
$result = array_diff($a, $c);
print_r($result);