Find value of sibling key in php array - php

I have an array in PHP, here is a snippet:
$locations = array(
array(
"id" => 202,
"name" => "GXP Club - Fable"
),
array (
"id" => 204,
"name" => "GXP Club - Gray"
)
);
What I know (from a GET) is the ID (202). What I would like to display is
"Showing results for "
( where $locations[?][id] = $_GET['id'] { echo $locations[?][name] } )
- if you will pardon my use of pseudo code.
Not sure what function is best or if I need to loop over the whole array to find that. Thanks.
Edit: for further clarification. I need to learn the [name] given the [id]

foreach( $locations as $arr ) {
if($arr['id'] == $_GET['id']) {
echo $arr['name'];
break;
}
}
That should do the trick.

While looping over the array is the solution for the problem as described, it seems more optimal to change your array to be $id=>$name key-value pairs, instead of named key values if that's all the data in the array, e.g.:
$locations = array( '202' => 'GXP Club - Fable',
'204' => 'GXP Club - Gray',
)
Alternatively, if there's more data, I'd switch to a nested data structure, e.g.:
$locations = array( '202' => array( 'name' => 'GXP Club - Fable', 'prop2' =>$prop2, etc),
'204' => array( 'name' => 'GXP Club - Gray', 'prop2' =>$prop2, etc),
)
That makes it so you can access data via ID (e.g. $locations[$id]['name']), which seems to be what you'd generally be wanting to do.

You can use array_map function which applies your custom action to each element in given array.
array_map(
function($arr) { if ($arr['id'] == $_GET['id']) echo $arr['name']; },
$locations
);

Doing this with PHP's built-in array functions* avoids a foreach loop:
<?php
$locations = [["id"=>202, "name"=>"GXP Club - Fable"], ["id"=>204, "name"=>"GXP Club - Gray"]];
$col = array_search(array_column($locations, "id"), $_GET["id"]);
echo $locations[$col]["name"];
Or, using a different method:
<?php
$locations = [["id"=>202, "name"=>"GXP Club - Fable"], ["id"=>204, "name"=>"GXP Club - Gray"]];
$result = array_filter($locations, function($v){ return $v["id"] == $_GET["id"]; });
echo array_shift($result)["name"];
* Notably, array_column() was not available until PHP 5.5, released 10 months after this question was asked!

Related

Simplify PHP array with same items

I have this PHP array:
$this->user_list = array( 0 => 'Not paid',1 => 'Not paid', 2 => 'Not paid', 7 => 'Waiting, 15 => 'Waiting', 10 => 'Cancelled' );
How can I simplify this array as the id numbers are different, but some of them have same status?
I tried it like this:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
but it doesn't work as expected.
Basically I want to achieve this:
echo $this->user_list[15] should give me Waiting, echo $this->user_list[10] should give me Cancelled, etc. So this is working in my first array very well, I am just thinking about grouping duplicate names there.
As mentioned by other contributors, there is no native support in the PHP grammar for your intended use case. As clearly stated in the PHP: Arrays documentation:
An array can be created using the array() language construct. It takes any number of comma-separated key => value pairs as arguments.
So basically each element in an array is a key => value pair, which means you cannot associate multiple keys to a single element.
This also explains why your first tentative didn't work:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
If you don't specify a key for an element, PHP uses a progressive index (0, 1, ...). So basically in the example above, the first zero is not actually a key, but a value, and PHP binds it to the key = 0. Maybe it could be easier for you to understand how it works if you print a var_dump or print_r of $this->user_list. You would get something similar to the following structure (NOTE: I have simplified the structure to make it more clear):
[
0 => [
0 => 0
1 => 1
2 => "Not paid"
],
1 => [
0 => 7,
15 => "Waiting"
],
10 => "Cancelled"
]
So how do we resolve this problem? Well... actually there is no need to contort the structure by swapping keys with values as other contributors seem to suggest. Changing the structure might simplify your "data entry" work but might also create big issues in other parts of the program because who knows, maybe accessing the invoice data by "ID" is simply more efficient than by "status" ... or something.
Since PHP does not provide such a feature out of the box, I believe a better solution would be to develop our own function; a good starting point could be the one in the example below.
function explode_array($config, $sep = ',') {
$res = [];
foreach($config as $configKey => $value) {
// split key values
$keys = explode($sep, $configKey);
foreach($keys as $key) {
$res[$key] = $value;
}
}
return $res;
}
$config = [
'0,1,2' => 'Not paid',
'7,15' => 'Waiting',
'10' => 'Cancelled'
];
$myArr = explode_array($config);
print_r($myArr);
The idea is quite simple: since we cannot use an array as key we leverage the next best data type, that is a CSV string. Please note there is no error handling in the above code, so the first thing you may want to do is adding some validation code to the explode_array (or however you wish to name it) function.
you should use like this. if id number is invoice id or something else and other value is there status about it.
$arr = array(
'Not paid' => [0,1,2] ,
'Waiting' => [5,6],
'Cancelled' =>[8]
);
foreach($arr as $key => $val){
foreach($val as $keys => $vals){
echo "invoiceid ".$vals ." status ".$key;
echo"<br>";
}
}
// for only one status you can use like this
foreach($arr['Not paid'] as $key => $val){
echo $val;
echo"<br>";
}
just try to run this and check output.
PHP has no built-in function or structure for handling cases like this. I'd use a simple array value-cloning function to map your duplicates. Simply have one instance of each status, then map the aliases, and then run a function that clones them in. As follows:
// Status list:
$ulist = [ 0 => 'Not paid', 7 => 'Waiting', 10 => 'Cancelled' ];
// Alternative IDs list, mapped to above source IDs:
$aliases = [ 0 => [1,2], 7 => [15] ];
// Function to clone array values:
function clone_values(array &$arr, array $aliases)
{
foreach($aliases as $src => $tgts) {
foreach($tgts as $tgt) {
$arr[$tgt] = $arr[$src];
}
}
ksort($arr); // If the order matters
}
// Let's clone:
clone_values($ulist, $aliases);
This results in the following array:
array(6) {
[0] · string(8) "Not paid"
[1] · string(8) "Not paid"
[2] · string(8) "Not paid"
[7] · string(7) "Waiting"
[10] · string(9) "Cancelled"
[15] · string(7) "Waiting"
}
....which can be accessed as you expect, here $ulist[2] => Not paid, etc. If the use case is as simple as illustrated in the OP, I'd personally just spell it out as is. There's no dramatic complexity to it. However, if you have dozens of aliases, mapping and cloning begins to make sense.
As said in the comments, you can't have multiple keys with one value. The best way is to use the keyword => [ number, number, number...] construction.
//set a result array
$result = [];
//loop the original array
foreach ( $this->user_list as $number => $keyword ){
//if the keyword doesn't exist in the result, create one
if(!isset ( $result [ $keyword ] ) ) $result[ $keyword ] = [];
//add the number to the keyword-array
$result[ $keyword ] [] = $number;
}

Merge/Append the differences between two 2d arrays to the intersection of the same two arrays to achieve a particular sort order

I have two arrays, the first one is the sorted version (sorted by the user) of the second one, but the second one can be shorter or longer than the first one. For example:
$books_sorted = array(
0 => array(
"title" => "In Search of Lost Time"
),
1 => array(
"title" => "Don Quixote"
),
2 => array(
"title" => "The Great Gatsby"
)
);
$books_available = array(
0 => array(
"title" => "Moby Dick"
),
1 => array(
"title" => "In Search of Lost Time"
),
2 => array(
"title" => "The Great Gatsby"
),
3 => array(
"title" => "War and Peace"
)
);
I need a result array that respects the order set by the user, but removes the missing books from the second array and adds them to the end of all the new books from the second array. Ex.
// "Don Quixote" is not available anymore -> needs to be removed
// "War and Peace" and "Moby Dick" are available -> need to be added both at the end
$books_third_array = array(
0 => array(
"title" => "In Search of Lost Time"
),
1 => array(
"title" => "The Great Gatsby"
),
2 => array(
"title" => "Moby Dick"
),
3 => array(
"title" => "War and Peace"
)
);
I only put the "title" key because there are others, but I don't think they're useful to this example.
You can find all elements in the first array that are in the second, then find all elements in the second that are not in the first - and combine the two. array_filter will help you there. You'll have something like this:
$sorted_titles = array_column($books_sorted, 'title');
$available_titles = array_column($books_available, 'title');
$third_array = array_merge(
array_filter($books_sorted, function($e) use ($available_titles) {
return in_array($e['title'], $available_titles);
}),
array_filter($books_available, function($e) use ($sorted_titles) {
return !in_array($e['title'], $sorted_titles);
})
);
Live demo: https://3v4l.org/fSpWm
Edit based on comments:
If you need to not just preserve other "fields" in your first array, but also copy non-existing keys from the second array into the first one, the code becomes somewhat more complicated. Something like this may do:
$sorted_titles = array_column($books_sorted, 'title');
$available_titles = array_reduce($books_available, function($result, $e) {
$result[$e['title']] = $e;
return $result;
});
$third_array = array_merge(
array_map(
function($e) use ($available_titles) {
return array_merge($available_titles[$e['title']], $e);
},
array_filter($books_sorted, function($e) use ($available_titles) {
return in_array($e['title'], array_keys($available_titles));
})
),
array_filter($books_available, function($e) use ($sorted_titles) {
return !in_array($e['title'], $sorted_titles);
})
);
Live demo: https://3v4l.org/VZGbB
Use usort() and define your own sorting-function, use the use keyword to pass in the array of sorted titles, and if its in the array, move it up - otherwise move it down.
$books_sorted_titles = array_column($books_sorted, 'title');
usort($books_available, function($k, $v) use ($books_sorted_titles) {
return in_array($v['title'], $books_sorted_titles) ? 1 : -1;
});
Live demo at https://3v4l.org/NsPtf
You should avoid making iterated calls of in_array() for performance reasons. When this logic is called for, use array_intersect().
To implement a functional-style approach, isolate the intersections between the arrays with the sorted array as the first parameter so that its order is preserved. Then get the diiferences between the arrays with the available array as the first parameter to return its qualifying elements. Then simply merge the arrays so that the sorted values come before the (un-chosen) available values.
Code: (Demo)
var_export(
array_merge(
array_uintersect(
$books_sorted,
$books_available,
fn($a, $b) => $a['title'] <=> $b['title']
),
array_udiff(
$books_available,
$books_sorted,
fn($a, $b) => $a['title'] <=> $b['title']
),
)
);
I don't know how this alternative snippet will compare in terms of performance, but you can determine the intersections and differences in one pass. This aproach however consumes/unsets elements from the available array. (Demo)
$available = array_column($books_available, 'title');
$result = [];
foreach ($books_sorted as $row) {
if (($index = array_search($row['title'], $available)) !== false) {
$result[] = $row;
unset($books_available[$index]);
}
}
array_push($result, ...$books_available);
var_export($result);

Array parts access

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.

Understanding the basics of multidimensional arrays

I am new to using multidimensional arrays with php, I have tried to stay away from them because they confused me, but now the time has come that I put them to good use. I have been trying to understand how they work and I am just not getting it.
What I am trying to do is populate results based on a string compare function, once I find some match to an 'item name', I would like the first slot to contain the 'item name', then I would like to increment the priority slot by 1.
So when when I'm all done populating my array, it is going to have a variety of different company names, each with their respective priority...
I am having trouble understanding how to declare and manipulate the following array:
$matches = array(
'name'=>array('somename'),
'priority'=>array($priority_level++)
);
So, in what you have, your variable $matches will point to a keyed array, the 'name' element of that array will be an indexed array with 1 entry 'somename', there will be a 'priority' entry with a value which is an indexed array with one entry = $priority_level.
I think, instead what you probably want is something like:
$matches[] = array(name => 'somename', $priority => $priority_level++);
That way, $matches is an indexed array, where each index holds a keyed array, so you could address them as:
$matches[0]['name'] and $matches[0]['priority'], which is more logical for most people.
Multi-dimensional arrays are easy. All they are is an array, where the elements are other arrays.
So, you could have 2 separate arrays:
$name = array('somename');
$priority = array(1);
Or you can have an array that has these 2 arrays as elements:
$matches = array(
'name' => array('somename'),
'priority' => array(1)
);
So, using $matches['name'] would be the same as using $name, they are both arrays, just stored differently.
echo $name[0]; //'somename';
echo $matches['name'][0]; //'somename';
So, to add another name to the $matches array, you can do this:
$matches['name'][] = 'Another Name';
$matches['priority'][] = 2;
print_r($matches); would output:
Array
(
[name] => Array
(
[0] => somename
[1] => Another Name
)
[priority] => Array
(
[0] => 1
[1] => 2
)
)
In this case, could this be also a solution with a single dimensional array?
$matches = array(
'company_1' => 0,
'company_2' => 0,
);
if (isset($matches['company_1'])) {
++$matches['company_1'];
} else {
$matches['company_1'] = 1;
}
It looks up whether the name is already in the list. If not, it sets an array_key for this value. If it finds an already existing value, it just raises the "priority".
In my opinion, an easier structure to work with would be something more like this one:
$matches = array(
array( 'name' => 'somename', 'priority' => $priority_level_for_this_match ),
array( 'name' => 'someothername', 'priority' => $priority_level_for_that_match )
)
To fill this array, start by making an empty one:
$matches = array();
Then, find all of your matches.
$match = array( 'name' => 'somename', 'priority' => $some_priority );
To add that array to your matches, just slap it on the end:
$matches[] = $match;
Once it's filled, you can easily iterate over it:
foreach($matches as $k => $v) {
// The value in this case is also an array, and can be indexed as such
echo( $v['name'] . ': ' . $v['priority'] . '<br>' );
}
You can also sort the matched arrays according to the priority:
function cmp($a, $b) {
if($a['priority'] == $b['priority'])
return 0;
return ($a['priority'] < $b['priority']) ? -1 : 1;
}
usort($matches, 'cmp');
(Sourced from this answer)
$matches['name'][0] --> 'somename'
$matches['priority'][0] ---> the incremented $priority_level value
Like David said in the comments on the question, it sounds like you're not using the right tool for the job. Try:
$priorities = array();
foreach($companies as $company) {
if (!isset($priorities[$company])) { $priorities[$company] = 0; }
$priorities[$company]++;
}
Then you can access the priorities by checking $priorities['SomeCompanyName'];.

Performing an action when finding same values in two PHP arrays

I have two associative arrays in PHP that are defined as following:
$this_week[] = array(
"top_song_id" => $row["view_song_id"],
"top_place" => $i,
"top_move" => "0",
"top_last" => $i,
"top_peak" => $i,
"top_rating" => get_song_rating_by_id($row["view_song_id"]),
"top_views" => $row["view_sum"],
"top_start" => $monday,
"top_end" => $sunday
);
and
$last_week[] = array(
"top_song_id" => $row["view_song_id"],
"top_place" => get_song_place_by_id($row["view_song_id"]),
"top_move" => "0",
"top_last" => get_song_last_by_id($row["view_song_id"]),
"top_peak" => get_song_peak_by_id($row["view_song_id"]),
"top_rating" => get_song_rating_by_id($row["view_song_id"]),
"top_views" => $row["view_sum"],
"top_start" => $prev_monday,
"top_end" => $prev_sunday
);
Now here is my question: how can I traverse this two arrays and perform an action if there is any song id in one array that can be found in the other one?
A for() loop doesn't work because there can be common songs for both arrays but not on the same array index.
Any help is appreciated.
An efficient way to do this is to just change the first line of the last snippet this way:
$last_week[$row["view_song_id"]] = array( // Added the song id as the array index
"top_song_id" => $row["view_song_id"],
...
After that you can use a simple for loop this way:
for ($this_week as $item) {
if ( isset ($last_week[ $item["top_song_id"] ]) ) {
// HERE YOU HAVE FOUND A DUPLICATE
}
}
if( in_array( $this_week["top_song_id"], $last_week ) ) {
//do something
}
Why not just hardcode the 5(?) comparisons you need in one if statement? No need to overcomplicate things.

Categories