I have a method in a class that looks like this;
class SomeClass {
private $hidden = array(....);
/**
* #return array - numeric indexed array in order of $this->hidden.
* Suitable for use by list(var1, var2, ...)
*/
public function getAsList($list = array())
{
return array_values(array_intersect_key($this->hidden, array_flip($list) );
}
But this is not useful, since the caller of the method does not know the order of the key/element pairs in the associative array in instance variable $hidden. Ideally, the returned array would be in the exact same order as the keys specified in $list. For example:
$foo = new SomeClass();
list($foo, $bar, $baz) = $foo->getAsList(array('foo', 'bar', 'baz');
I can easily write some explicit, verbose PHP code in a loop to do this, but is there some clever way to use the various array functions, e.g. array_multisort() to spit this out in minimal lines of code (and hopefully, at compiled code speed -- I'll test it, if it matters).
In a sense, this is a brain teaser to which I don't yet know the answer. It's not critical I do it without an explicit loop, but I'm curious as to if it can be done. I spent 30 or so minutes on it, and haven't found a solution yet.
Perhaps array_replace is the missing piece to your puzzle:
public function getAsList($list = array())
{
$klist = array_flip($list);
return array_values(array_intersect_key(array_replace($klist, $this->hidden), $klist));
}
Example (Demo):
$hidden = [
'apples' => 19,
'eggs' => 7,
'grapes' => 144,
'mushrooms' => 3,
'oranges' => 16
];
$list = ['grapes', 'apples', 'eggs', 'oranges'];
$klist = array_flip($list);
print_r(array_values(array_intersect_key(array_replace($klist, $hidden), $klist)));
/*
Array
(
[0] => 144
[1] => 19
[2] => 7
[3] => 16
)
*/
This is one of those cases when functional programming pales in comparison to a language construct in terms of readability, maintanability, and efficiency.
I have a bias toward functional programming, but in this case it just doesn't pay.
Code: (Demo)
$hidden = [
'apples' => 19,
'eggs' => 7,
'grapes' => 144,
'mushrooms' => 3,
'oranges' => 16
];
$list = ['grapes', 'apples', 'eggs', 'oranges'];
foreach ($list as $item) {
if (isset($hidden[$item])) {
$result[] = $hidden[$item];
}
}
var_export($result);
Output:
array (
0 => 144,
1 => 19,
2 => 7,
3 => 16,
)
If you insist on using functional programming, then it would be most efficient to perform the required operations in this order:
filter the array
order the keys of the filtered array
reindex the ordered, filtered array
Here is how:
Code: (Demo)
$flippedList = array_flip($list);
var_export(array_values(array_replace($flippedList, array_intersect_key($hidden, $flippedList))));
(same output as previous snippet)
Logically, it doesn't make sense to order an array that has excess elements in it. Lighten the load, first.
Related
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 );
I have three arrays, say multiarray, valsarray, and otherarray. otherarray is a multidimensional array that supplies values to multiarray and valsarray, but besides that it is unimportant here. valsarray takes values from a subarray of each value in otherarray and multiarray takes straight values from otherarray, as demonstrated below:
foreach($otherarray as $other){
foreach($other as $sub){
$valsarray[] = $sub
}
$multiarray[] = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
}
Now what I would like to do is append each key/value pair in valsarray to the current array entry of multiarray, to achieve a result similar to:
$multiarray = array('Val1' => $other['Val1'], 'Val2' => $other['Val2'],
'VALSARRAY_KEY1' => VALSARRAY_VALUE1, ..., 'VALSARRAY_KEYN' => VALSARRAY_VALUEN)
I have attempted to solve this using current in the following fashion:
foreach($valsarray as $key => $val){
current($multiarray)[$key] = $val;
}
But the multiarray remained unaltered. I may be misunderstanding how current works, or how to approach this problem, so any help or direction would be appreciated.
EDIT- EXAMPLE
otherarray = array(...prior array entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
'Val3' => 'not important',
'Val4' => array(0 => 'subA', 1 => 'subB'),
...next array entries...);
BEFORE MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde'));
valsarray = array(0 => 'subA', 1 => 'subB');
AFTER MERGE:
multiarray = array(...prior entries...,
array('Val1' => 'abc',
'Val2' => 'cde',
0 => 'subA',
1 => 'subB'));
So if multiarray was a regular array instead of a multidimensional one, I would do something like:
foreach($valsarray as $key => $val){
$multiarray[$key] = $val;
}
To achieve the end result.
I am not 100% sure what you are trying to accomplish a Minimal, Complete, and Verifiable example may help if I have misunderstood something.
It appears that the current() function does not work as you assume. (Or more specifically, the internal pointer.)
If you look at the example in the PHP documentation: Current(), you will see that for current($array) to change elements, you need to call next($array) or prev($array).
These function move the internal pointer of the array.
Note that in PHP 5, foreach loops use the internal pointer (and reset it when you start a loop), but in PHP 7, foreach loops do not use the internal pointer.
Anyway, here is my best guess at what could help you.
$valsarray_index = 0;
foreach ($otherarray as $other) {
$multiarray_value = array('Val1' => $other['Val1'], 'Val2' => $other['Val2']);
foreach ($other as $sub) {
$multiarray_value[$valsarray_index] = $sub;
// $multiarray_value["VALSARRAY_KEY" . $valsarray_index] = $sub;
$valsarray[] = $sub;
$valsarray_index += 1; // This stays in lockstep with the last index of $valsarray
}
$multiarray[] = $multiarray_value;
}
I am not exactly sure about what you want the final output to look like. If this produces incorrect information, then if would be helpful to provide some specific arrays for input and what you expect as output.
So far all my research has shown that this cannot be achieved without writing lengthy functions such as the solution here
Surely there is a simpler way of achieving this using the predefined PHP functions?
Just to be clear, I am trying to do the following:
$test = array(
'bla' => 123,
'bla2' => 1234,
'bla3' => 12345
);
// Call some cool function here and return the array where the
// the element with key 'bla2' has been shifted to the beginning like so
print_r($test);
// Prints bla2=1234, bla=>123 etc...
I have looked at using the following functions but have so far have not been able to write a solution myself.
array_unshift
array_merge
To Summarize
I would like to:
Move an element to the beginning of an array
... whilst maintaining the associative array keys
This seems, funny, to me. But here ya go:
$test = array(
'bla' => 123,
'bla2' => 1234,
'bla3' => 12345
);
//store value of key we want to move
$tmp = $test['bla2'];
//now remove this from the original array
unset($test['bla2']);
//then create a new array with the requested index at the beginning
$new = array_merge(array('bla2' => $tmp), $test);
print_r($new);
Output looks like:
Array
(
[bla2] => 1234
[bla] => 123
[bla3] => 12345
)
You could turn this into a simple function that takes-in a key and an array, then outputs the newly sorted array.
UPDATE
I'm not sure why I didn't default to using uksort, but you can do this a bit cleaner:
$test = array(
'bla' => 123,
'bla2' => 1234,
'bla3' => 12345
);
//create a function to handle sorting by keys
function sortStuff($a, $b) {
if ($a === 'bla2') {
return -1;
}
return 1;
}
//sort by keys using user-defined function
uksort($test, 'sortStuff');
print_r($test);
This returns the same output as the code above.
This isn't strictly the answer to Ben's question (is that bad?) - but this is optimised for bringing a list of items to the top of the list.
/**
* Moves any values that exist in the crumb array to the top of values
* #param $values array of options with id as key
* #param $crumbs array of crumbs with id as key
* #return array
* #fixme - need to move to crumb Class
*/
public static function crumbsToTop($values, $crumbs) {
$top = array();
foreach ($crumbs AS $key => $crumb) {
if (isset($values[$key])) {
$top[$key] = $values[$key];
unset($values[$key]);
}
}
return $top + $values;
}
I'm trying to understand arrays better. Pardon my elementary questions as I just opened my first php book three weeks ago.
I get that you can retrieve key/value pairs using a foreach (or for loop) as below.
$stockprices= array("Google"=>"800", "Apple"=>"400", "Microsoft"=>"4", "RIM"=>"15", "Facebook"=>"30");
foreach ($stockprices as $key =>$price)
What I get confused about are multi dimensional arrays like this one:
$states=(array([0]=>array("capital"=> "Sacramento", "joined_union"=>1850, "population_rank"=> 1),
[1]=>array("capital"=> "Austin", "joined_union"=>1845,"population_rank"=> 2),
[2]=>array("capital"=> "Boston", "joined_union"=>1788,"population_rank"=> 14)
));
My first question is really basic: I know that "capital', "joined_union", "population_rank" are keys and "Sacramento", "1850", "1" are values (correct?). But what do you call [0][1][2]? Are they "main keys" and "capital" etc. sub-keys? I can't find any definition for those; neither in books nor online.
The main question is how do I retrieve Arrays [0][1][2]? Say I want to get the array that joined_union in 1845 (or even trickier during the 1800s), then remove that array.
Finally, can I name Arrays [0][1][2] as California, Texas and Massachusetts correspondingly?
$states=(array("California"=>array("capital"=> "Sacramento", "joined_union"=>1850, "population_rank"=> 1),
"Texas"=>array("capital"=> "Austin", "joined_union"=>1845,"population_rank"=> 2),
"Massachusetts"=>array("capital"=> "Boston", "joined_union"=>1788,"population_rank"=> 14)
));
Unlike other languages, arrays in PHP can use numeric or string keys. You choose.
(This is not a well loved feature of PHP and other languages sneer!)
$states = array(
"California" => array(
"capital" => "Sacramento",
"joined_union" => 1850,
"population_rank" => 1
),
"Texas" => array(
"capital" => "Austin",
"joined_union" => 1845,
"population_rank" => 2
),
"Massachusetts" => array(
"capital" => "Boston",
"joined_union" => 1788,
"population_rank" => 14
)
);
As for querying the structure you have, there are two ways
1) Looping
$joined1850_loop = array();
foreach( $states as $stateName => $stateData ) {
if( $stateData['joined_union'] == 1850 ) {
$joined1850_loop[$stateName] = $stateData;
}
}
print_r( $joined1850_loop );
/*
Array
(
[California] => Array
(
[capital] => Sacramento
[joined_union] => 1850
[population_rank] => 1
)
)
*/
2) Using the array_filter function:
$joined1850 = array_filter(
$states,
function( $state ) {
return $state['joined_union'] == 1850;
}
);
print_r( $joined1850 );
/*
Array
(
[California] => Array
(
[capital] => Sacramento
[joined_union] => 1850
[population_rank] => 1
)
)
*/
-
$joined1800s = array_filter(
$states,
function ( $state ){
return $state['joined_union'] >= 1800 && $state['joined_union'] < 1900;
}
);
print_r( $joined1800s );
/*
Array
(
[California] => Array
(
[capital] => Sacramento
[joined_union] => 1850
[population_rank] => 1
)
[Texas] => Array
(
[capital] => Austin
[joined_union] => 1845
[population_rank] => 2
)
)
*/
1: Multidimensional arrays are basically 'arrays of arrays'.
So if we look here:
array("0"=>array("capital"=> "Sacramento", "joined_union"=>1850, "population_rank"=> 1)
0 is the key, and the array is the value.
Then, inside the value, you have capital as a key, and Sacramento as a value.
2: For removing arrays: Delete an element from an array
3: State names for keys
Yes, you can change that 0, 1, 2 into state names. They become key-values instead of a numbered array. Which makes it much easier to remove exactly the one you want to remove.
Multidimensional arrays are simply arrays which have arrays as values. Simple or "scalar" types are types likes int, string and bool, they hold one value and one value only. Arrays are a compound type, meaning it's a thing that combines several other things together. An array can contain values of other types, including arrays.
The simplest visualization is probably this longhand:
$array = array('foo' => array('bar' => 'baz'));
$foo = $array['foo']; // $foo is now array('bar' => 'baz')
echo $foo['bar']; // outputs 'baz'
This is just shorthand for the same thing as above:
echo $array['foo']['bar'];
$array['foo'] gives you access to the value array('bar' => 'baz'), ['bar'] on that array gives you the value 'baz'. It doesn't matter whether you assign the intermediate value into a variable or continue directly.
The other way around, here's a demonstration of the concept that multidimensional arrays are just arrays in arrays:
$baz = 'baz';
$bar = array('bar' => $baz);
$array = array('foo' => $bar);
That's all there is to it.
You can call nested arrays in array "sub arrays", though there's no real definition and it's not really necessary to define this, since nested arrays aren't a special case of anything.
You may stumble upon explanations using the terms "two dimensional", "three dimensional" etc. arrays. Do not imagine this as tables and cubes, because it's not accurate and will make your head explode when it goes beyond three dimensions. This "dimensionality" simple refers to the depth of the array, i.e. how many arrays are nested within each other.
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 );