PHP, loop through dynamic array by count - php

So I am working on a JQuery Step Form. It is being populated with dynamic information from the database. I have a particular list of items that is 21 items in the array. I would like to break these down into groups of 5 or 6 per step, so the form isn't so long.
I know I could use array_slice for example, but since this list is dynamic, I don't know how many there will always be. It will kind of be paginated in a sense but I don't need pagination links and such. Just a way to return 5 items, then 5 items and if the last iteration only has 2 items then that is it.
So for example:
$array = (
array(
"name" => "Peter Parker",
"alterego" => "Spiderman",
),
array(
"name" => "Clark Kent",
"alterego" => "Superman",
),
array(
"name" => "Bruce Wayne",
"alterego" => "Batman",
),
);
And then I want to break this down to only list 2 items per group.

You can use array_chunk for example:
$chunks = array_chunk($array, 5);
foreach ($chunks as $chunk) {
// display form for these 5 values
}

Andreas' initial advice is great. Maybe here, we might want to design some callback functions based on the pagination sizes that we wish to have:
array_slice($array, getChunkStart($array), getChunkLength($array));
or if we just look for a specific column size:
array_chunk($array, sizeof(array_column($array, "column_key")));

Related

How to detect and avoid infinite loop in PHP

This question is not about a bug in my PHP code (for the moment I don't have any PHP script, I am still thinking about the algorithm). Here is the problem:
I'm currently working on a mechanical piece manager which would be able to build a mechanical piece based on internal part (for example, I got a piece which is a Bike, and for that piece, I need 2 wheels, 1 handlebar, and for a wheel I need a tire etc).
Each internal part is also a mechanical piece in my database and is linked with a unique ID (and has a folder which contains many PDF, many 3D files, etc).
I got a GUI (in HTML) representing my database and each piece has a "Build" button to gather all files required to build internal piece.
For example:
My bike has the ID n°1, a wheel has the ID n°2 and the handlebar has the ID n°3.
The algorithm is pretty simple but vulnerable with infinite loop.
How could I do to avoid this following case: What if my bike (id 1) need a wheel (id 2), and my wheel need a bike...which needs a wheel which need a bike......?
Thank you very much,
During the execution of your build function, you would just keep track of all components that you have already produced a result for -- in a hash --, and if you encounter one of those again, you just ignore it.
Here is some boilerplate code you could use for inspiration:
// Sample "database":
$components = array(
1 => array (
"id" => 1,
"name" => "bike",
"needs" => array (
array ("id" => 2, "count" => 2), // 2 wheels
array ("id" => 3, "count" => 1), // 1 handlebar
),
"folder" => "/my/folders/bike"
),
2 => array(
"id" => 2,
"name" => "weel",
"needs" => array(
array("id" => 4, "count" => 1), // 1 tire
array("id" => 1, "count" => 1) // 1 wheel?? - erroneous information!
),
"folder" => "/my/folders/wheel"
),
3 => array(
"id" => 3,
"name" => "handlebar",
"needs" => array (),
"folder" => "/my/folders/handlebar"
),
4 => array(
"id" => 4,
"name" => "tire",
"needs" => array(),
"folder" => "/my/folders/tire"
)
);
// Build function: returns a list of folders related
// to all the parts of the given component.
function build($componentId, $components, $hash = array()) {
// Protection against infinite recursion:
if (isset($hash[$componentId])) return [];
$elem = $components[$componentId];
// set hash, keyed by component ID.
$hash[$componentId] = 1;
// Add folder for this component
$folders[] = $elem['folder'];
// Collect folders of dependent components recursively
foreach($elem['needs'] as $child ) {
// ... pass the hash as third argument
$folders = array_merge($folders, build($child["id"], $components, $hash));
}
return $folders;
}
// Example call: build component with ID 1, returning a list of folders:
print_r (build(1, $components));
The output of the above code would be:
Array
(
[0] => /my/folders/bike
[1] => /my/folders/wheel
[2] => /my/folders/tire
[3] => /my/folders/handlebar
)
So when the bike was encountered a second time, it was just ignored.
You should differentiate the child-parent relations.
A bike can contain a wheel as a child item and a wheel may have a bike as its parent item. Also one screw and tire can be child items of the wheel.
Navigation should have one direction, either from parent items to child items or the opposite.

PHP selecting only 4 from multidimensional array and shuffling

I have the following PHP multidimensional array, I'm looking to try select 4 random items and then show them with the title, image and text. With the code I've used I seem to get a single number which is randomized and not what I need.
<?php
$arr = array(
array(
"image" => "",
"title" => "Open 7 days.",
"text" => "We’re open 7 days a week."
),
array(
"image" => "",
"title" => "Well done",
"text" => "Well done you done great."
),
array(
"image" => "",
"title" => "Rice",
"text" => "Various flavours"
),
array(
"image" => "",
"title" => "Rooms",
"text" => "Roomy rooms for a roomyful time"
),
array(
"image" => "",
"title" => "Keep in touch.",
"text" => "Stay in touchwith us as we'll miss you"
),
array(
"image" => "",
"title" => "Location",
"text" => "We'll show you where we are."
),
array(
"image" => "",
"title" => "The Home",
"text" => "See our home page"
)
);
print_r(array_rand($arr));
If you're picking only one entry, array_rand() returns a single key for a random entry. If you use the num to specify how many keys should be picked, then it returns num number of keys of random entries.
The function only returns the keys of the random entries and not the array chunks itself. You'll have to manually build the array from the returned keys:
// get the random keys
$keys = array_rand($arr, 4);
// initialize result array
$result = array();
// loop through the keys and build the array
foreach ($keys as $k) {
$result[] = $arr[$k];
}
print_r($result);
Update
From a quick benchmark, it seems array_rand() is signficantly faster than using shuffle() for larger arrays. The benchmark was done on an array having 14336 elements with 10000 iterations each.
The results obtained on my dev machine were as follows:
Number of elements in the array - 14336
Number of iterations for each method - 10000
array_rand() version took 4.659 seconds
shuffle() version took 15.071 seconds
The code used for benchmarking can be found in this gist.
No need to loop !
shuffle() and array_slice() does the job.
Simply shuffle your array such that it rearranges the entries , now pick the first 4 items using array_slice.
shuffle($arr);
print_r(array_slice($arr,0,4));
Demo
Read about the $num parameter array_rand()
print_r(array_rand($arr, 4));
To display all:
foreach(array_rand($arr, 4) as $key) {
echo $arr[$key]['text'] ."\n";
//etc
}

Accessing multi-dimensional, unordered, associative arrays in an ordered fashion using an incremental loop

This is primarily a PHP answer, but the methodology may in fact be language agnostic.
After running a CSV file through a simple parser, I was given a multidimensional array that resembled something akin to this:
array( 'estimated' =>
array(
array( "TITLE" => 'MAIN', "CURR_PERF" => 100, "POT_PERF" => 75 ),
array( "TITLE" => 'HEAT', "CURR_PERF" => 90, "POT_PERF" => 60 ),
array( "TITLE" => 'CO2', "CURR_PERF" => 56, "POT_PERF" => 40 ),
),
'actual' =>
array(
array( "TITLE" => 'MAIN', "CURR_PERF" => 100, "POT_PERF" => 75 ),
array( "TITLE" => 'HEAT', "CURR_PERF" => 89 , "POT_PERF" => 75),
array( "TITLE" => 'CO2', "CURR_PERF" => 40, "POT_PERF" => 20 ),
);
);
Now, horrific data structure to one side, without refactoring of the underlying parser - how would be the best way to ensure that you can access these in a specific order? Without necessarily touching the underlying parser?
If you loop through using a for()/foreach() loop you're only going to be able to read them in a linear order - increasing or decreasing down the elements. You're not necessarily going to be able to drill down and get the specific value required.
For instance, the CSV file could express the values for estimated in a different order to the values for actual; and it may be required that they are output in yet another order.
For example, here are three different orders off the top of my head:
-> MAIN HEAT CO2
-> HEAT MAIN CO2
-> CO2 HEAT MAIN
Furthermore, as is quite usual, the label names in the CSV file are not exactly user-friendly - so they need to be 'translated' (if you like) in to something more human-friendly. Naturally, without the use of lots of if() statements if preferable!
Given it's a very specific use case, but it's something I've seen before with regards to arrays being serialised - and often finding out that they are actually nested.
I've posted one possible solution, but would be happy to see others. (In the past when I've done similar I've never accepted my own answer ;)) I'm sure there must be a more elegant way than the one I've devised..!
This was the quickest (and neatest) solution I could come up with on the spot at the time, but it's not one that I take very much pride in..! It involves multiple loops, the use of several arrays and seems to be an over-engineered mechanism for doing something which should surely be pretty simplistic?
First of all I've created an associative array to use as a dictionary to look up translations for text strings found in the CSV file:
$tbl = array( "MAIN"=>"Main Costs",
"TOTAL"=>"Total Costs",
"CO2"=>"Gas expended" );
Next up, I created an array to use as an 'index' - I entered the key values here in the order I would like them to be accessed in the application:
$index = array( "MAIN", "TOTAL", "CO2" );
I then created two blank arrays and populated them with the data from the child arrays, by using a loop I was able to make them associative - allowing me to specify use the TITLE fields as keys:
$estimated = array();
$actual = array();
foreach( $bills['estimated'] as $bill ){
$estimated[ $bill['title'] ] = $bill;
}
foreach( $bills['actual'] as $bill ){
$actual[ $bill['title'] ] = $bill;
}
By doing this I could loop through them in a specific order, regardless of the order they were parsed in, like so:
for($i=0; $i<3; $i++){
$bill = $estimated[ $index[ $i ] ];
printf(" %s: %d ", $tbl[ $index[ $i ] ], $bill['CURR_PERF'] );
}
Which would output the following, in the order I specified:
// 1. Main Costs: 100
// 2. Total Costs: 90
// 3. Gas Expended: 56
Naturally, this order could easily be changed if required. It does however:
require using an array specifically to act as an index
require using two loops simply to initialise
uses a total of four extra arrays
If this array was assigned to variable $ary like:
$ary = array(
'estimated' =>
array(
array('TITLE' => 'MAIN', 'CURR_PERF' => 100, 'POT_PERF' => 75),
array('TITLE' => 'HEAT', 'CURR_PERF' => 90, 'POT_PERF' => 60),
array('TITLE' => 'CO2', 'CURR_PERF' => 56, 'POT_PERF' => 40),
),
'actual' =>
array(
array('TITLE' => 'MAIN', 'CURR_PERF' => 100, 'POT_PERF' => 75),
array( 'TITLE' => 'HEAT', 'CURR_PERF' => 89 , 'POT_PERF' => 75),
array( 'TITLE' => 'CO2', 'CURR_PERF' => 40, 'POT_PERF' => 20 ),
);
);
Your estimated array could be accessed like:
$newVar = $ary['estimated'][2]['CURR_PERF'];
$newVar would be 56. You can reassign a value as well, like:
$ary['estimated'][0]['POT_PERF'] = 300;
That first array in the estimated array used to be 75 and is now 300.
This is how you get and set specific values in a Multi-dimensional Array.
To sort the arrays, if you need to loop over and maintain their indexes, see PHP's uasort() function. This may take some work, on your part, to develop a cmp_function, but should do what you're looking for in a different way.
See,
http://www.php.net/manual/en/function.uasort.php
and
http://www.php.net/manual/en/function.usort.php ,
which explains cmp_function better.

associating two separate arrays for itinerary system

I am trying to build a travel itinerary system. The user selects the dates of travel, and then may add items to each day.
I have an array of dates, stored in a session in the format:
array(
(int) 0 => '2012-08-25',
(int) 1 => '2012-08-26',
(int) 2 => '2012-08-27'
)
They will then choose attractions, which I wish to store in an array in the format:
array(
(int) 0 => array(
'Attraction' => array(
'attraction_id' =>'1',
'name' => 'Place One',
)
),
(int) 1 => array(
'Attraction' => array(
'attraction_id' => '2',
'name' => 'Place Two',
)
),
I'd like to be able to output:
2012-08-25
Place One
Place Two
2012-08-26
nothing here yet!
2012-08-27
nothing here yet!
So, each item of the first array contains an array of items, if that makes sense. I am struggling with the logic of associating the keys of the days array with the items array.
I looked at array_merge but that doesn't seem to do what I need.
Is there an easy way to achieve this?
This code does exactly what you ask. Unfortunately, I fear your question doesn't reflect your aim given the example. Using keys to link data will led to 1-1 relationship, where as you seem to need a 1-n. You should have a foreign key field in the attraction array, like date_id.
$array= array();
foreach($dates as $date_key=>$date){
$array[$date]=array();
foreach($attractions as $attraction_key=>$attraction){
if($date_key==$attraction_key){
$array[$date][]=$attraction['Attraction']['name'];
}
}
}

Will foreach become inefficient when number of array elements grow big?

Currently I'm using foreach to search key when use array_replace:
$grades = array(
0 =>array('id'=>1, 'grade'=>4),
1 =>array('id'=>5, 'grade'=>2),
2 =>array('id'=>17,'grade'=>1),
)
$replacement = array('id'=>17,'grade'=>3);
foreach($grades as $key=>$grade){
if($grade->id ==$replacement['id'] )
$found = $key;
}
$new_grades = array_replace($grades, array($found_key=>$replacement));
I wonder if this will become inefficient when the number of elements grow too much in $grades array. Is there any better way to do the search and replace job?
The execution time grows linearly with the number of elements in the array (O(N)). Use a better data structure, i.e. use the array in an associative way with the ID as index:
$grades = array(
1 => array('grade'=>4),
5 => array('grade'=>2),
17 => array('grade'=>1)
);
Then the lookup cost is constant (O(1)). You can do:
$grades[$replacement['id']] = array('grade' => $replacement['grade']);
or something similar, depending on your data.
Yeah, that can be done vastly more efficiently.
$grades = array(
1 => 4,
5 => 2,
17 => 1,
);
$replacement = array(
17 => 3,
);
$grades = array_merge($grades, $replacement);
If you need more information associated with the ID than just the grade, then you'll still need a more involved data structure like Felix Kling has. But no such requirement is present in your question so I'm not assuming it.

Categories