PHP merging arrays based on specific array key - php

I have 2 arrays that I want to merge based on a key's value in array 1. In the below example, I want the game_modes put into the games_list based on a key (id) in games_list.
Array 1 which is pulled from a table full of games:
$games_list = array(
0 => array(
'id' => 23,
'name' => 'Call of Duty: Modern Warfare 3'
),
2 => array(
'id' => 1,
'name' => 'Call of Duty: Black Ops'
)
);
Array 2 which is pulled from a table full of game modes:
$game_modes = array(
0 => array(
'id' => 1,
'game_id' => 1,
'description' => 'Capture the Flag'
),
1 => array(
'id' => 2,
'game_id' => 1,
'description => 'Domination'
),
2 => array(
'id' => 3,
'game_id' => 23,
'description' => 'Kill Confirmed'
)
);
I would like the result to be:
$games_list = array(
0 => array(
'id' => 23,
'name' => 'Call of Duty: Modern Warfare 3'
'modes' => array(
array(
'id' => 3,
'game_id' => 23,
'description' => 'Kill Confirmed'
)
)
),
2 => array(
'id' => 1,
'name' => 'Call of Duty: Black Ops'
'modes'=> array(
0 => array(
'id' => 1,
'game_id' => 1,
'description' => 'Capture the Flag'
),
1 => array(
'id' => 2,
'game_id' => 1,
'description => 'Domination'
)
)
)
);
Additional info, the site I'm working on currently has 71 games in its database and each game could have some arbitrary number of game modes.
Now, I could easily do a bunch of for loops and avoid this question all together. As of right now I don't have ton of game modes entered into the database but I keep adding more all the time. Over time doing exponentially more loops all the time will eventually make the page load speed come to a crawl.
I have taken the time to place this data into memcache to make future calls quicker avoiding the loop.
I've never been good with array_map as I don't quite understand how it works or if its even the right route.

Don't you think query level solution would be better?
The lengthy way would be:
// array: $game_modes;
// array: $game_lists;
foreach ($game_modes as $gm=>$modes){
if (isset($modes['game_id'])){
foreach ($game_lists as $gl=>$lists){
if ($lists['id'] == $modes['game_id']){
$game_lists[$gl]['modes'][] = $modes;
//break;
}
}
}
}
Output Category : Summary
$query = 'SELECT
g.id, g.name_name,
group_concat(gm.description) as descriptions
FROM games as g
LEFT JOIN games_modes as gm
ON g.id = gm.game_id
GROUP BY g.id';
Result:
id | name | descriptions
------------------------------------------------------------
1 | Call of Duty: Black Ops | Capture the Flag, Domination
Output Category : Detail
$query = 'SELECT
g.id, g.name_name,
gm.id, gm.description
FROM games as g
LEFT JOIN games_modes as gm
ON g.id = gm.game_id
ORDER BY g.id';
Result:
id | name | id | description
----- --------------------------- ------- ------------------
1 | Call of Duty: Black Ops | 1 | Capture the Flag
1 | Call of Duty: Black Ops | 2 | Domination

Try this to decrease looping
$cachearray = array();
foreach ($game_modes as $gm=>$modes){
if(array_key_exists($modes['game_id'],$cachearray))
{
$cachearray[$modes['game_id']]['modes'][] = $modes;
}
else
foreach ($games_list as $gl=>$lists){
if ($lists['id'] == $modes['game_id']){
$games_list[$gl]['modes'][] = $modes;
$cachearray[$lists['id']] = &$games_list[$gl];
break;
}
}
}
print_r($games_list);
It will be easier if you have $games_list array like this
$games_list = array(
23 => array(
'id' => 23,
'name' => 'Call of Duty: Modern Warfare 3'
),
1 => array(
'id' => 1,
'name' => 'Call of Duty: Black Ops'
)
);
And will more powerfull by using query

If you want to user array map, this code should be possible:
$ids = array_map(function($game) { return $game['id']; }, $games_list);
$id_mapping = array_flip($ids);
foreach($game_modes as $mode) {
if (array_key_exists($mode['game_id'], $id_mapping)) {
$games_list[$id_mapping[$mode['game_id']]]['modes'][] = $mode;
}
}
But I do not know whether this is faster than the two for loops.

try this:
$mode_map = array();
foreach($game_modes as $mode)
{
$game_id = $mode['game_id'];
if(!isset($mode_map[$game_id]))
{
$mode_map[$game_id] = array();
}
$mode_map[$game_id][] = $mode;
}
foreach($games_list as &$game)
{
$game_id = $game['id'];
if(isset($mode_map[$game_id]))
{
$game['modes'] = $mode_map[$game_id];
}
}
print_r($games_list);
result:
Array
(
[0] => Array
(
[id] => 23
[name] => Call of Duty: Modern Warfare 3
[modes] => Array
(
[0] => Array
(
[id] => 3
[game_id] => 23
[description] => Kill Confirmed
)
)
)
[2] => Array
(
[id] => 1
[name] => Call of Duty: Black Ops
[modes] => Array
(
[0] => Array
(
[id] => 1
[game_id] => 1
[description] => Capture the Flag
)
[1] => Array
(
[id] => 2
[game_id] => 1
[description] => Domination
)
)
)
)

Related

Loop into multidimensional array from top to bottom

I have this tree :
Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
),
[1] => Array
(
[id] => 5
[parent_id] => 1
[title] => Sub Page 2
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)
And I'm looking for a display from top to bottom.
And display something like this :
1
1.2
1.5
4
But if I have id 3 which is a leaf from 5 I would like this :
1
1.2
1.5
1.5.3
4
I have search a lot and my brain is limited when i'm using recursivity..
I have tried this :
function printAll($a){
foreach ($a as $v){
if (!array_key_exists('children', $v)){
debugLog($v['id']);
return;
}
else{
$arrayChildrens = $v['children'];
foreach($arrayChildrens as $c){
$arrayChildrens = $c['children'];
$this->printAll($arrayChildrens);
}
}
}
}
But doesn't work..
I tried to begin just to display
1
2
5
4
But my goal is to display id parents before id ( like Ishowed you before)
Thanks a lot !
This function should give you your expected output.
function printAll($a, $prefix = '') {
//loop through $a
foreach($a as $v) {
//echo current level `id` with previous `$prefix`
echo "{$prefix}{$v['id']}\n";
//check if current level contains children
if(!empty($v['children'])) {
//clean up prefix to remove extra `.` at the end of prefixes
$prev_prefix = rtrim($prefix, '.');
//recurse printAll again passing the children as `$a` and a `$prefix` being the previous levels combined e.g `1.5`
//also clean up extra periods at the start of the prefix
printAll($v['children'], ltrim("{$prev_prefix}.{$v['id']}.", "."));
}
}
}
Output:
1
1.2
1.5
1.5.3
4
Using a proper return
Usually with a function you actually want the function to return values instead of echoing them automatically to your page. If you want this function to return an array of values instead of echoing them, you could do this:
function printAll($a, $level = '', $values = []) {
foreach($a as $v) {
$values[] = $value = "{$level}{$v['id']}";
if(!empty($v['children'])) {
$values = printAll($v['children'], "{$value}.", $values);
}
}
return $values;
}
Which will have a result like this:
Array
(
[0] => 1
[1] => 1.2
[2] => 1.5
[3] => 1.5.3
[4] => 4
)
This should do the job.
$arr = array(
array(
'id' => 1,
'parent_id' => 0,
'title' => 'Parent Page',
'children' => array(
array(
'id' => 2,
'parent_id' => 1,
'title' => 'Sub Page',
),
array(
'id' => 5,
'parent_id' => 1,
'title' => 'Sub Page 2',
'children' => array(
array(
'id' => 7,
'parent_id' => 5,
'title' => 'Sub Page',
),
array(
'id' => 8,
'parent_id' => 5,
'title' => 'Sub Page 2',
)
)
)
)
),
array(
'id' => 4,
'parent_id' => 0,
'title' => 'Another Parent Page',
)
);
function printAll($arr, $parent = [])
{
if (is_array($arr)) {
foreach ($arr as $k => $v) {
if (isset($v['id'])) {
$parent[] = $v['id'];
echo implode('.', $parent) . PHP_EOL;
}
if (isset($v['children'])) {
printAll($v['children'], $parent);
}
array_pop($parent);
}
}
}
printAll($arr);
Output
1
1.2
1.5
1.5.7
1.5.8
4
Working demo.

Php count rows and division into a group

Users create accounts and they are assigned to a group, group limit is set in advance, let's assume 5.
Example: First 5 users has been saved to group_id = 1. Than how to make the 6th user have been saved to the 2nd group?
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => 1
]);
How to make this dynamic for some groups, like group_id = 3..4..5..6 etc ?
Example code:
$groupSize = 2;
$allUserGroups = 4;
if($groupSize < $allUserGroups) {
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => 1
]);
} else {
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => 2
]);
}
This is only work with two groups - how to make this dynamic ?
Thanks guys!
<?php
$arrayUsers=['user1','user2','user3','user4','user5',
'user6','user7','user8','user9','user10',
'user11','user12','user13','user14','user15'];
$group_id=1;
echo'<pre>';
$groupedUser=array_chunk($arrayUsers, 5);
foreach($groupedUser as $data ){
$newArray[$group_id]=$data ;
$group_id++;
}
print_r($newArray);
So let's assume that your users are in the array i created. array_chunk will split them in groups of the number you want. As you said in your question i splitted them in groups of 5. Then to create dynamic groups i just used a counter which i increment by one everytime my array field was full (5 records). In the output you will get a multidimensional array with keys the group_id and values the users.
Array
(
[1] => Array
(
[0] => user1
[1] => user2
[2] => user3
[3] => user4
[4] => user5
)
[2] => Array
(
[0] => user6
[1] => user7
[2] => user8
[3] => user9
[4] => user10
)
[3] => Array
(
[0] => user11
[1] => user12
[2] => user13
[3] => user14
[4] => user15
)
)
if i understand your problem your are looking for this solution :-
$getgroups = UserGroups::select('group_id',DB::raw('count(group_id) as count'))->groupby('group_id')->get();
$getgroups = json_decode(json_encode($getgroups),true);
if(!empty($getgroups)){
end($getgroups);
$key = key($getgroups);
$group = $getgroups[$key];
if($group['count'] <5){
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => $group['group_id']
]);
}else{
$groupvalue = $group['group_id'] +1;
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => $groupvalue
]);
}
}else{
UserGroups::insert([
'user_id' => $user,
'game_id' => $gameId,
'group_id' => 1
]);
}
echo "saved"; die;
I have tested its working perfectly. Good luck for your project :)

Copy array key into another existing array key

I have 2 arrays, one with key, another with numeric keys, how can i copy they key, replacing the numeric keys in the exact order?
Array with numeric key
Array
(
[0] => ABCDEFG
[1] => This is my description
[2] => 12.00
[3] => 30.00
[4] => My supplier
[5] => My brand
[6] => Shoes
[7] =>
)
Array 2
Array
(
[productcode] => Product Code
[productitemdesc] => Description
[retailsalesprice] => Selling Price
[currentcost] => Unit Cost
[supplier] => Supplier
[productbrand] => Brand
[productcategory] => Category
[productgroup] => Group
)
I would want something like this
Array
(
[productcode] => ABCDEFG
[productitemdesc] => This is my description
[retailsalesprice] => 12.00
[currentcost] => 30.00
[supplier] => My Supplier
[productbrand] => My Brand
[productcategory] => Shoes
[productgroup] =>
)
Is there any existing functions available for php? Tried array_fill_keys but doesn't seem to be what i want.
You can use the function array_combine() to combine the keys from the second array (for the following example called $array_keys) with the values from the first array (called $array_values):
Example:
$combined = array_combine(array_keys($array_keys), $array_values);
print_r($combined);
This prints out the array exactly like you described.
array_combine way is a lot better but you can use this function as well. This will allow you to modify the values if you want.
function custom_combine($numeric_array, $keyed_array)
{
$temp = array();
$i=0;
foreach($keyed_array as $key=>$val)
{
if(isset($numeric_array[$i]))
$temp[$key] = $numeric_array[$i];
else
$temp[$key] ='';
$i++;
}
return($temp);
}
The other answers are definitely more efficient, but in case you'd like to learn how to manually loop through arrays, something like this should work:
<?php
// The original array
$arr1 = array(
0 => 'ABCDEFG',
1 => 'This is my description',
2 => '12.00',
3 => '30.00',
4 => 'My supplier',
5 => 'My brand',
6 => 'Shoes',
7 => '',
);
// The second array
$arr2 = array(
'productcode' => 'Product Code',
'productitemdesc' => 'Description',
'retailsalesprice' => 'Selling Price',
'currentcost' => 'Unit Cost',
'supplier' => 'Supplier',
'productbrand' => 'Brand',
'productcategory' => 'Category',
'productgroup' => 'Group',
);
// Pre-define the new array to avoid errors
$arr_new = array();
// Manually create a value to increment during our foreach loop
$increment = 0;
// Loop through each value in $arr2
foreach ($arr2 as $key2 => $value2) {
// If the key is set in $arr1, assign the value from $arr1 and the key from $arr2
// to the new array
if (isset($arr1[$increment])) {
$arr_new[$key2] = $arr1[$increment];
}
// Increment the value regardless of whether it was found in $arr1 or not
$increment++;
}
// Remove this if you want... It just displays the values found in $arr_new
print_r($arr_new);
i tested it and work :
<?php
$a=array(
0 => "ABCDEFG",
1 => "This is my description",
2 => "12.00",
3 => '30.00',
4 => 'My supplier',
5 => 'My brand',
6 => 'Shoes',
7 => '',
)
;
$b=array
(
'productcode' => 'Product Code',
'productitemdesc' => 'Description',
'retailsalesprice' => 'Selling Price',
'currentcost' => 'Unit Cost',
'supplier' => 'Supplier',
'productbrand' => 'Brand',
'productcategory' => 'Category',
'productgroup' => 'Group',
);
$j=0;
foreach ($b as $i => $value) {
$b[$i]=$a[$j];
$j++;
}
var_dump($b);
?>
you can use array_combine()
$array3=array_combine($array2,$array1);
print_r($array3);

Appending multiple values to associative array in PHP [duplicate]

This question already has answers here:
Merge two 2d arrays by shared column value
(6 answers)
Closed last month.
I would like to create a PHP array that will end up looking like this:
idPlayer namePlayer Points
1 John 20
2 Sam 25
3 Ben 22
But I would like to append the values not all at once:
First append the idPlayer and namePlayer
Then append the points for that idPlayer (I would have a $idPlayer variable to use each time I loop)
How could I do this in PHP?
I was thinking:
$myArray['idPlayer'] = "1";
$myArray['namePlayer'] = "John";
$myArray['Points'] = "20"
And then, how would I tell the array to go to the next row?
// output array
$myArray = array();
// your loop
while (something) {
// make an array for this iteration
$itarr = array();
// put stuff in it
$itarr['idPlayer'] = "1";
$itarr['namePlayer'] = "John";
$itarr['Points'] = "20"
// append it to output array using standard array indexing
$myArray[] = $itarr;
// OR, your own index
$myArray[$itarr['idPlayer']] = $itarr;
}
I dont know why you want to achieve such thing. But consider this example:
// like player table array initial
$players = array(
array(
'idPlayer' => 1,
'namePlayer' => 'John',
),
array(
'idPlayer' => 2,
'namePlayer' => 'Sam',
),
array(
'idPlayer' => 3,
'namePlayer' => 'Ben',
),
);
// data points to be added later (like points table)
$points = array(
array(
'idPlayer' => 1,
'Points' => 20,
),
array(
'idPlayer' => 2,
'Points' => 25,
),
array(
'idPlayer' => 3,
'Points' => 22,
),
);
$myArray = array();
foreach($players as $key => $value) {
foreach($points as $index => $element) {
// if this particular id matches the record inside points table then merge
if($value['idPlayer'] == $element['idPlayer']) {
$myArray[] = array('idPlayer' => $value['idPlayer'], 'namePlayer' => $value['namePlayer'], 'Points' => $element['Points']);
}
}
}
Should output something like: (can be used in a tabular data)
Array
(
[0] => Array
(
[idPlayer] => 1
[namePlayer] => John
[Points] => 20
)
[1] => Array
(
[idPlayer] => 2
[namePlayer] => Sam
[Points] => 25
)
[2] => Array
(
[idPlayer] => 3
[namePlayer] => Ben
[Points] => 22
)
)
Use the idPlayer as index:
$players = [
1 => [
'namePlayer' => 'John',
'points' => 20,
],
5 => [
'namePlayer' => 'Seth',
'points' => 25,
],
13 => [
'namePlayer' => 'Ben',
'points' => 35,
],
];
var_dump($players);
http://codepad.viper-7.com/nSXmZF
In php <5.4 use array() instead of [] constructor.

Isolate an array column containing indexed rows of data, then flatten/merge to form an array of rows

situation
I have an array result returned from an Database call. In the example below, it fetches many Genres which can have many books. Using a join, the query pulls the books from each genre at the same time. Here is a hypothetical result set:
array(
[0] => array (
'id' => 1,
'title' => 'ficton'
'modules' => array(
[0] => array(
'other_id' => 1
'other_title' => 'James Clavell'
),
[1] => array(
'other_id' => 2
'other_title' => 'Terry Pratchett'
),
[2] => array(
'other_id' => 3
'other_title' => 'Robert Ludlum'
),
),
[1] => array (
'id' => 2,
'title' => 'non-ficton'
'modules' => array(
[1] => array(
'other_id' => 5
'other_title' => 'An excessive book of excessively interesting things'
),
[2] => array(
'other_id' => 6
'other_title' => 'It\'s late, I can\'t think of what to put here'
),
)
)
)
Situation
What I would like to end up with is an array which contains only the modules as shown below:
array(
[0] => array(
'other_id' => 1
'other_title' => 'James Clavell'
),
[1] => array(
'other_id' => 2
'other_title' => 'Terry Pratchett'
),
[2] => array(
'other_id' => 3
'other_title' => 'Robert Ludlum'
),
[3] => array(
'other_id' => 5
'other_title' => 'An excessive book of excessively interesting things'
),
[4] => array(
'other_id' => 6
'other_title' => 'It\'s late, I can\'t think of what to put here'
)
)
Problem
Now, I have no problem achieving this through iteration but, feel there is a much better (undiscovered) means to achieving this.
Question
Is a shortcut to creating the desired result. The code I have so far is listed below and it's not a difficult situation to solve. I'm more just curious as to whether there is a much BETTER version of doing the following.
Ugly code that works
Here is a version of the code that 100% works but, features more iteration than I could care for.
$aryTemp = array();
foreach($aryGenres as $intKey => $aryGenre) {
foreach($aryGenre['modules'] as $aryModule) {
$aryTemp[] = $aryModule
}
}
Attempt using array map
An attempt using array map and failing horribly
$aryTemp = array();
foreach($aryGenres as $intKey => $aryGenre) {
$aryTemp[] = array_map(
function($aryRun) { return $aryRun;
},$aryGenre['modules']
}
I would love to be able to cut out the foreach loop as shown above.
PHP 5.6+:
$modules = array_merge(...array_column($arr, 'modules'));
# Allowing empty array
$modules = array_merge([], ...array_column($arr, 'modules'));
PHP 5.5:
$modules = call_user_func_array('array_merge', array_column($arr, 'modules'));
PHP ~5.4:
$modules = call_user_func_array(
'array_merge',
array_map(
function ($i) {
return $i['modules'];
},
$arr
)
);

Categories