I am trying to get away from doing things manually and repetitively by correctly utilizing loops and functions (methods) in oop programming; but I have hit a major stumbling block as it regards to multidimensional array groups, in passing the correct values to the necessary abstracted function (method) responsible for a database action.
Any help at all is very much welcomed and will enable me to move on from this stumbling block that I have been trying to push away for days upon days but without progress and it is out of true frustration and much agony that I am here begging for help.
Below is the code that for simplicity I have shortened as much as possible (can be easily tested locally by copying and pasting):
// array with table properties and form values - start
$form_fields_arr = [
'group' => [
'anime' => [ // genre
'table_prop' => [ // for update query - table properties
'table_name' => 'anime_tbl',
'account_id' => 2,
'visible' => 'yes'
],
'form_data' => [ // for update query - form values
'2' => 'Attack on Titan',
'4' => 'RWBY',
'6' => 'Rurouni Kenshin',
'8' => 'A Silent Voice'
]
],
'movie' => [ // genre
'table_prop' => [ // for update query - table properties
'table_name' => 'movie_tbl',
'account_id' => 4,
'visible' => 'yes'
],
'form_data' => [ // for update query - form values
'1' => 'Queen of Katwe',
'3' => 'Forest Gump',
'5' => 'War Horse',
'7' => 'The Fault in our Stars'
]
]
]
]; // ... end
// loop through multidimensional array and pass values to function - start
foreach ($form_fields_arr['group'] as $frm_key_1 => $frm_val_1) { // 2d array
foreach ($frm_val_1 as $frm_key_2 => $frm_val_2) { // 1d array
if (strcasecmp($frm_key_1, $frm_key_1) === 0) { // group by genre
foreach ($frm_val_2 as $frm_key_3 => $frm_val_3) { // 1d array
if (strcasecmp($frm_key_2, 'form_data') === 0) {
$title = $form_fields_arr['group'][$frm_key_1]['form_data'][$frm_key_3]; // anime/movie title
}
if (isset($frm_val_2['table_name']) &&
isset($frm_val_2['account_id']) &&
isset($frm_val_2['visible']) &&
isset($title)
) {
dbUpdate(
$frm_val_2['table_name'],
$frm_val_2['account_id'],
$frm_val_2['visible'],
$title
);
}
} // 1d array
} // if block
} // 1d array
} // 2d array
// ... end
// function that receives passed values - start
function dbUpdate($table_name, $account_id, $title_col, $form_value) {
$test_val_arr = [$table_name, $account_id, $title_col, $form_value];
return print_r($test_val_arr);
} // ... end
The above code outputs:
// array values passed to and returned from function
Array (
[0] = movie_tbl
[1] = 4
[2] = yes
[3] = A Silent Voice
)
Array (
[0] = movie_tbl
[1] = 4
[2] = yes
[3] = A Silent Voice
)
Array (
[0] = movie_tbl
[1] = 4
[2] = yes
[3] = A Silent Voice
)
Array (
[0] = movie_tbl
[1] = 4
[2] = yes
[3] = A Silent Voice
)
But the desired result that I am trying to achieve is:
// for anime genre - array values passed to and returned from function
Array (
[0] = anime_tbl
[1] = 2
[2] = yes
[3] = Attack on Titan
)
Array (
[0] = anime_tbl
[1] = 2
[2] = yes
[3] = RWBY
)
Array (
[0] = anime_tbl
[1] = 2
[2] = yes
[3] = Rurouni Kenshin
)
Array (
[0] = anime_tbl
[1] = 2
[2] = yes
[3] = A Silent Voice
)
// for movie genre - array values passed to and returned from function
Array (
[0] = movies_tbl
[1] = 4
[2] = yes
[3] = Queen of Katwe
)
Array (
[0] = movies_tbl
[1] = 4
[2] = yes
[3] = Forest Gump
)
Array (
[0] = movies_tbl
[1] = 4
[2] = yes
[3] = War Horse
)
Array (
[0] = movies_tbl
[1] = 4
[2] = yes
[3] = The Fault in our Stars
)
so upon everything royally failing with me spending literally about a week trying to fix this, telling myself that it is very simple and I really shouldn't be stuck here, out of desperation I decided to go back to my repetitive ways and tried the following:
// new array without table properties - start
$new_array = [];
$new_array['group']['anime'] = $form_fields_arr['group']['anime']['form_data'];
$new_array['group']['movie'] = $form_fields_arr['group']['movie']['form_data']; // ... end
// loop through multidimensional array and pass values to function - start
foreach ($new_array['group'] as $key_1 => $val_1) { // 2d array
foreach ($val_1 as $key_2 => $val_2) { // 1d array
if (strcasecmp($key_1, $key_1) === 0) {
dbUpdate('anime_tbl', 2, 'yes', $val_2);
dbUpdate('movie_tbl', 4, 'yes', $val_2);
} // if block
} // 1d array
} // 2d array
// ... end
But the results are still very much undesirable. Everything was working fine until I started using multidimensional arrays, simply because I realized that utilizing multidimensional arrays help me to shorten my code in other areas considerably. But I am stuck here and will have to go back further up and undo quite a lot of changes if I can't get this to work. I am pleading for help from any good soul out there. Please help me someone! Anyone!
I am being optimistic here and assuming that if by any chance I do get some help in fixing the above problem, could someone please also teach me how to loop through an array structure like the one below while yet getting the desired results without duplicates (I have truly tried but have truly failed):
// array with table properties and form values - start
$form_fields_arr = [
'table_prop' => [ // table properties group
'anime' => [ // for update query - table properties
'table_name' => 'anime_tbl',
'account_id' => 2,
'visible' => 'yes'
],
'movie' => [ // for update query - table properties
'table_name' => 'movie_tbl',
'account_id' => 4,
'visible' => 'yes'
]
],
'form_data' => [ // for update query - form values
'anime' => [ // genre
'2' => 'Attack on Titan',
'4' => 'RWBY',
'6' => 'Rurouni Kenshin',
'8' => 'A Silent Voice'
],
'movie' => [ // genre
'1' => 'Queen of Katwe',
'3' => 'Forest Gump',
'5' => 'War Horse',
'7' => 'The Fault in our Stars'
]
]
]; // ... end
You got a logic mistake in your for loops. First of all your variable namings are not very intuitive. $frm_key_1, $frm_key_2, etc. look alike and force the reader to have the array structure in mind all the time to understand the variables meaning. This led to a mistake like this one: if( strcasecmp($frm_key_1, $frm_key_1) === 0 ). This is always true.
Then you had two exclusive conditions:
if (strcasecmp($frm_key_2, 'form_data') === 0)
And:
if (isset($frm_val_2['table_name']) && /* ... */) {
If $frm_key_2 is 'form_data' you are in the second child of the genre array, yet the fields 'table_name', etc. are defined only in the first one (witht the key 'table_prop'). So both conditions can never be true at the same time.
Your condition to trigger the dbUpdate() function was, that all fields of the 'table_prop' array were present (which you iterated through at the same time), and a $title was set aswell. This was only true after your third for-loop iterated for the second time. During that iterations the $title variable got overwritten constantly, but no sbUpdate() was triggered, because $frm_val_2 had the values from 'form_data' instead of 'table_prop'. So after the 3rd for loop finished the 2nd time $title was 'A Silent Voice', which is simply the last child of the first 'form_data' array. Afterwards your 2nd for loop iterated the 2nd 'table_prop' array again, which means that now the 'dbUpdate()' condition was true, so it postet 4 times (number of childs in the 'table_prop' array) the parameters with $title = 'A Silent Voice'.
You tried to make everything as generic as possible, making everything over complicated. The best solution that works here is one that respects the specific structure.
This works:
<?php
// array with table properties and form values - start
$form_fields_arr = [
'group' => [
'anime' => [ // genre
'table_prop' => [ // for update query - table properties
'table_name' => 'anime_tbl',
'account_id' => 2,
'visible' => 'yes'
],
'form_data' => [ // for update query - form values
'2' => 'Attack on Titan',
'4' => 'RWBY',
'6' => 'Rurouni Kenshin',
'8' => 'A Silent Voice'
]
],
'movie' => [ // genre
'table_prop' => [ // for update query - table properties
'table_name' => 'movie_tbl',
'account_id' => 4,
'visible' => 'yes'
],
'form_data' => [ // for update query - form values
'1' => 'Queen of Katwe',
'3' => 'Forest Gump',
'5' => 'War Horse',
'7' => 'The Fault in our Stars'
]
]
]
];
// loop through multidimensional array and pass values to function - start
foreach ($form_fields_arr['group'] as $genreData) {
$tableProperties = $genreData['table_prop'];
if (!isset($tableProperties['table_name'])
|| !isset($tableProperties['account_id'])
|| !isset($tableProperties['visible'])) {
continue;
}
$data = $genreData['form_data'];
foreach ($data as $title) {
dbUpdate(
$tableProperties['table_name'],
$tableProperties['account_id'],
$tableProperties['visible'],
$title
);
}
}
// function that receives passed values - start
function dbUpdate($table_name, $account_id, $title_col, $form_value) {
$test_val_arr = [$table_name, $account_id, $title_col, $form_value];
return print_r($test_val_arr);
} // ... end
For the last part of the question that wasn't answered, thanks to Philipp Maurer's answer, after playing around with the code I got it to work. I am just placing the answer here for anyone who might have a similar problem and would like to better understand how to group and fetch values from a multidimensional array using a foreach loop without duplicates or incorrect results. See below code:
// array with table properties and form values - start
$form_fields_arr = [
'table_prop' => [ // table properties group
'anime' => [ // for update query - table properties
'table_name' => 'anime_tbl',
'account_id' => 2,
'visible' => 'yes'
],
'movie' => [ // for update query - table properties
'table_name' => 'movie_tbl',
'account_id' => 4,
'visible' => 'yes'
]
],
'form_data' => [ // for update query - form values
'anime' => [ // genre
'2' => 'Attack on Titan',
'4' => 'RWBY',
'6' => 'Rurouni Kenshin',
'8' => 'A Silent Voice'
],
'movie' => [ // genre
'1' => 'Queen of Katwe',
'3' => 'Forest Gump',
'5' => 'War Horse',
'7' => 'The Fault in our Stars'
]
]
]; // ... end
// loop through multidimensional array and pass values to function - start
foreach ($form_fields_arr as $index => $group_array) {
foreach ($group_array as $genre_key => $genre_val) {
if (!isset($group_array[$genre_key]['table_name']) ||
!isset($group_array[$genre_key]['account_id']) ||
!isset($group_array[$genre_key]['visible'])
) {
continue;
}
foreach ($form_fields_arr['form_data'][$genre_key] as $data_key => $data_title) {
dbUpdate(
$group_array[$genre_key]['table_name'],
$group_array[$genre_key]['account_id'],
$group_array[$genre_key]['visible'],
$data_title
);
}
}
}
// ... end
// function that receives passed values - start
function dbUpdate($table_name, $account_id, $title_col, $form_value) {
$test_val_arr = [$table_name, $account_id, $title_col, $form_value];
return print_r($test_val_arr);
} // ... end
Related
I have the following array to show menu's based on the order the user specified.
The array is as follows:
$menuArray = [
'Main Street' => [
['/index.php', 'Home'],
['/city.php', $cityData[$user->city][0]],
['/travel.php', 'Travel'],
['/bank.php', 'Bank'],
['/inventory.php', 'Inventory'],
['/dailies.php', 'Dailies'],
],
'Activities' => [
(!$my->hospital) ? ['/hospital.php', 'Hospital'] : [],
(!$my->hospital && !$my->prison) ? ['/crime.php', 'Crime'] : [],
['/missions.php', 'Missions'],
['/achievements.php', 'Achievements'],
],
'Services' => [
['/hospital.php', 'Hospital'],
['/prison.php', 'Prison'],
['/search.php', 'Search'],
],
'Account' => [
['/edit_account.php', 'Edit Account'],
['/notepad.php', 'Notepad'],
['/logout.php', 'Logout'],
]
];
I have a column menu_order stored in the database, which has a default value of 0,1,2,3,4, but this can change per user as they will be able to change their menu to their likes.
What I'd like to achieve:
0 => Main Street
1 => Activities
2 => Services
3 => Account
4 => Communication
To get the menu order, I do
$menuOrder = explode(',', $user->menu_order);
But I'm not sure how to handle the foreach for displaying the menu.
Here's one way to do it -- use replacement rather than a sorting algorithm.
Code: (Demo)
$menuArray = [
'Main Street' => [],
'Activities' => [],
'Services' => [],
'Account' => []
];
$lookup = [
0 => 'Main Street',
1 => 'Activities',
2 => 'Services',
3 => 'Account',
4 => 'Communication'
];
$customsort = '4,2,1,3,0';
$keys = array_flip(explode(',', $customsort)); convert string to keyed array
//var_export($keys);
$ordered_keys = array_flip(array_replace($keys, $lookup)); // apply $lookup values to keys, then invert key-value relationship
//var_export($ordered_keys);
$filtered_keys = array_intersect_key($ordered_keys, $menuArray); // remove items not on the current menu ('Communication" in this case)
//var_export($filtered_keys);
$final = array_replace($filtered_keys, $menuArray); // apply menu data to ordered&filtered keys
var_export($final);
Output:
array (
'Services' =>
array (
),
'Activities' =>
array (
),
'Account' =>
array (
),
'Main Street' =>
array (
),
)
And here's another way using uksort() and a spaceship operator:
$ordered_keys = array_flip(array_values(array_replace(array_flip(explode(',', $customsort)), $lookup)));
uksort($menuArray, function($a, $b) use ($ordered_keys) {
return $ordered_keys[$a] <=> $ordered_keys[$b];
});
var_export($menuArray);
As a consequence of how your are storing your custom sort order, most of the code involved is merely to set up the "map"/"lookup" data.
You could try something like this to produce the menu:
function display_menu($menus, $m) {
if (!isset($menus[$m])) return;
echo "<ul>";
foreach ($menus[$m] as $item) {
if (!count($item)) continue;
echo "<li>{$item[1]}\n";
}
echo "</ul>";
}
$menuMap = array(0 => 'Main Street',
1 => 'Activities',
2 => 'Services',
3 => 'Account',
4 => 'Communication');
$menuOrder = explode(',', $user->menu_order);
foreach ($menuOrder as $menuIndex) {
$thisMenu = $menuMap[$menuIndex];
display_menu($menuArray, $thisMenu);
}
Small demo on 3v4l.org
I have a problem to view some data in the way I want to have it.
Here is a example of the array:
$items =
0 => [
'name' => 'foo'
'description' => 'bar'
'url' => 'http://foobar.com'
'headline' => 'Headline 1'
],
1 => [
'name' => 'uni'
'description' => 'corn'
'url' => 'http://unicorn.com'
'headline' => 'Headline 1'
],
2 => [
'name' => 'awe'
'description' => 'some'
'url' => 'http://awesome.com'
'headline' => 'Headline 2'
],
And know I want to loop through the items array and want to show the headline at first and all items that have the same headline. If a item has another headline, I want to print out the other headline and the items that belongs to it.
Should look like that:
Headline 1 : <--- Items that do have this headline
name = foo
description = bar
url = http://foobar.com
name = uni
description = corn
url = http://unicorn.com
Headline 2 <----- items with a new headline
name = awe
description = some
url = http://awesome.com
I wansn't able to do that. Can someone help me there?
I've tried something like a for loop that checks the current headline with the next headline.
#for ($i = 0; $i <= count($items); $i++)
<span>{{ $items[$i]['headline'] }}</span>
#if($items[$i]['headline'] == $items[$i+1]['headline'])
.....
# else .....
#endfor
But this haven't worked well
Thanks for your help and sorry because of my bad english!
Use laravel collections with groupby() method
$collection = collect($items);
$items= $collection->groupBy('headline');
$items->toArray();
The array will be splited by headline
From Laravel Docs https://laravel.com/docs/5.4/collections#method-groupby
If your array was a collection before converting to an array, you could use groupBy() collection method:
$collection->groupBy('headline');
Maybe this could help. i am writing this code in core PHP
$arr = array();
foreach($items as $item) {
$arr[$item['headline']] = $item;
}
it will return you an array of something like
Array
(
[Headline 1] => Array
(
[name] => uni
[description] => corn
[url] => http://unicorn.com
[headline] => Headline 1
)
[Headline 2] => Array
(
[name] => awe
[description] => some
[url] => http://awesome.com
[headline] => Headline 2
)
)
I have two sets of array ,first array contains all categories called "all", and second array contains selected categories called "selected", I want to populate this concept to multiple combo box,
$all = [
0 => [
'id'=>1,
'name' => 'news'
],
1 => [
'id'=>2,
'name' => 'tips'
],
2 => [
'id'=>3,
'name' => 'trick'
],
3 => [
'id'=>4,
'name' => 'review'
]
];
$selected = [
0 => [
'id'=>2,
'name' => 'trick'
],
1 => [
'id'=>4,
'name' => 'review'
],
];
I've try to do foreach in foreach , but i have duplicated data when show in combo box, i want to have all data from "all" shown with selected data from "selected".
i just solved my problem in deferent way , first i add default pair of key and value "sel"=>0 in "all" array set, then i loop trough array "all" and array "sel" to get similar value and when it match change sel key to 1 ,this code for further explanation
public static function compare($sel,$all){
// add sel key with default value = 0
foreach($all as $k=>$v){
$all[$k]['sel'] = 0;
}
foreach($all as $k=>$v){
foreach($sel as $k2=>$v2){
// when match change sel to 1
if($v['id'] == $v2['id']){
$all[$k]['sel'] = 1;
}
}
}
return $all;
}
final result :
$all = [
0 => [
'id'=>1,
'name' => 'news',
'sel' => 0
],
1 => [
'id'=>2,
'name' => 'tips',
'sel' => 0
],
2 => [
'id'=>3,
'name' => 'trick',
'sel' => 1
],
3 => [
'id'=>4,
'name' => 'review',
'sel' => 1
]
];
just add if condition when $all['sel'] = 1 they should be selected, thanks all :D
You can get the intersection of both arrays with array_uintersect and a custom callback function (compare).
function compare($a, $b){
if($a['id'] == $b['id']){
return 0;
}
return 1;
}
$res = array_uintersect($selected, $all,"compare");
print_r($res);
>Array ( [0] => Array ( [id] => 2 [name] => trick ) [1] => Array ( [id] => 4 [name] => review ) )
After that you only need to loop through the final array and set the corresponding check boxes.
If you want to compare by name just create another callback function.
function compare2($a, $b){
if($a['name'] == $b['name']){
return 0;
}
return 1;
}
The duplicates are caused by the inner for loop continuing to create select elements even after it has found a selected element. You can avoid having an inner loop and using php's in_array() function to check if $all is in $selected like this:
$x = '';
foreach($all as $a){
if(in_array($a, $selected)){
$x .= '<option selected>'.$a['id'].'Selected </option>';
}else{
$x .= '<option>'.$a['id'].'Not selected </option>';
}
}
echo $x;
Note that in_array will check all values of the elements, so for example element with id 2 but different name will appear as not selected. You may want to change both names to tips. I hope that helps.
I need to do a query and get certain kind of data. I have 2 tables, users and connections, I need to get per user how many times he/she connected per month and year.
users connections
........... ................
john 10/02/2014
john 15/02/2014
john 03/01/2015
john 06/02/2015
Is there a chance to get this info in this format:
john=>
[0]=>2014
[0]=>02
'total' =>2
[1]=>2015
[0]=>01
'total' => 1
[1]=>02
'total' => 2
[2]=>03
'total'=> 1
I'm using Codeigniter and also PHP.
Answering to #CodeGodie what I've done so far is:
public function getPeriodicity(){
$this->db->select('u.vusr_user, extract (MONTH from (to_timestamp(c.vuc_log_in))) as month, extract (YEAR from (to_timestamp(c.vuc_log_in))) as yearly, COUNT(c.vuc_log_in)');
$this->db->from('vts_users_conn c');
$this->db->join('vts_users u', 'c.vuc_vusr_id = u.vusr_id');
$this->db->group_by('u.vusr_user, month, yearly','asc');
$query = $this->db->get();
return $query->result_array();
}
Assuming you are using Codeigniter's $this->db->result_array() to obtain your database results, your initial array will look like this:
$res = array(
array(
"name" => "john",
"date" => "10/02/2014"
),
array(
"name" => "john",
"date" => "15/02/2014"
),
array(
"name" => "john",
"date" => "03/01/2015"
),
array(
"name" => "john",
"date" => "06/02/2015"
),
array(
"name" => "john",
"date" => "06/03/2015"
)
);
In order to change this array to your desired output, I would do the following:
foreach ($res as $row) {
$date_arr = explode("/", $row['date']);
$n = $row['name'];
$y = $date_arr[2];
$m = $date_arr[1];
if (!isset($final[$n]))
$final[$n] = array();
if (!isset($final[$n][$y]))
$final[$n][$y] = array();
if (!isset($final[$n][$y][$m])) {
$final[$n][$y][$m] = array("total" => 1);
} else {
$final[$n][$y][$m]["total"] = $final[$n][$y][$m]["total"] + 1;
}
}
If you var_dump your final result (var_dump($final)), you will get the following:
array (size=1)
'john' =>
array (size=2)
2014 =>
array (size=1)
'02' =>
array (size=1)
'total' => int 2
2015 =>
array (size=3)
'01' =>
array (size=1)
'total' => int 1
'02' =>
array (size=1)
'total' => int 1
'03' =>
array (size=1)
'total' => int 1
Hope this helps.
As a general rule, if you can access the data and see in your mind how you want that data to look, then it's pretty much possible to get it to do that. It's just a matter of working out the process.
In your case, I would do the following steps:
Order the data by users, then by date so everything is nicely together
Loop through the data and each time, check that the current user is the same as the last one. if it's not, create a new array key
split the date into the parts you want
check the user array for the key relating to year for that user. If the year exists, search for the month. If the month exists, add 1 to the total for that month. If the year and/or month don't exist, create the keys and set the total to be 1 for that month
Once the records have been processed, you should have the data in the format you need.
I've written this script to be run in PHP, and am trying to convert it to Ruby.
Code:
$cases = array();
foreach($results as $result)
{
if(!array_key_exists($result['id'], $cases))
$cases[$result['id']] = array($result);
else
$cases[$result['id']][] = $result;
}
foreach($cases as $key => $case)
{
foreach($case as $payment)
{
if(count($case) > 1)
{
$cases[$key]['total'] += ($payment['p1'] > $payment['p2']) ? $payment['p1'] : $payment['p2'];
} else {
$cases[$key]['total'] = ($payment['p1'] > $payment['p2']) ? $payment['p1'] : $payment['p2'];
}
}
}
In Ruby, I have the results as an array returned by MySQL (using mysql2 gem). Using the loop below, it prints every row. How can I check if the key exists when it does not provide keys?
What's the best way to implement this code in Ruby? How can I add a hash onto a hash in Ruby similar to adding an array onto an array in PHP.
results.each(:as => :array) do |row|
puts row.inspect
end
More explanation:
PHP Structure
Array(
0 => Array(
Array(
[id] => 'random id',
[p1] => 534,
[p2] => 105
),
Array(
[id] => 'random id',
[p1] => 335,
[p2] => 425
)
),
1 => Array(
Array(
[id] => 'random id',
[p1] => 259,
[p2] => 124
)
)
)
And i'm trying to iterate over each array inside the first, and if p1 > p2, add p1 to the total key on that array. i.e., the 0 index would look like this:
0 => Array(
'total' => 959,
Array(
[id] => 'random id',
[p1] => 534,
[p2] => 105
),
Array(
[id] => 'random id',
[p1] => 335,
[p2] => 425
)
)
Are you trying to group by result['id']? I believe that "translating" your PHP code to ruby will look something like this:
cases = {}
results.each do |result|
if cases[result['id']]
cases[result['id']] << result
else
cases[result['id']] = [result]
end
end
Another way to get the same result is to use group_by:
cases = results.group_by { |result| result['id'] }
The second part "translated" to ruby will look something like this:
cases.each do |key, c|
c.each do |payment|
if c.count > 1
c['total'] += (payment['p1'] > payment['p2']) ? payment['p1'] : payment['p2']
else
c['total'] = (payment['p1'] > payment['p2']) ? payment['p1'] : payment['p2']
end
end
end
(this will actually not work in ruby, since an array cannot have an arbitrary key like 'total' - it accepts only numbers in its brackets. You will have to modify this code to hold the totals in a different structure)
Again, a more idiomatic way of summing up the max between 'p1' and 'p2' of each payment in each case might look more like this:
totals = cases.values.map do |c|
c.inject(0) { |sum, payment| sum + [payment['p1'], payment['p2']].max }
end