Combine arrays in PHP - php

Trying to learn arrays in PHP. Snippets posted for brevity.
HTML form here:
<p>What are your favorite type of cookies?</p>
<input type="checkbox" name="cookies[]" value="Oreos" />Oreos<br />
<input type="checkbox" name="cookies[]" value="Chocolate chip" />Chocolate chip<br />
<input type="checkbox" name="cookies[]" value="Sugar" />Sugar<br />
<input type="checkbox" name="cookies[]" value="Vanilla Mocha" />Vanilla Mocha<br />
<p>What are your favorite type of drinks?</p>
<input type="checkbox" name="drinks[]" value="Soda" />Soda<br />
<input type="checkbox" name="drinks[]" value="Wine" />Wine<br />
<input type="checkbox" name="drinks[]" value="Milk" />Milk<br />
<input type="checkbox" name="drinks[]" value="Water" />Water<br />
PHP page here:
foreach ($drinks as $d) {
echo "Your favorite drink(s) are: " . $d . "<br />";
}
foreach ($cookies as $cookie) {
echo "Your favorite cookies are: " . $cookie . "<br />";
}
$experimentalArray = array($cookie => $d);
foreach ($experimentalArray as $key => $value) {
echo "Cookie - " . $key . " Drink - " . $value . "<br /><br />";
}
Both cookies and drinks are multi-choice questions, so you can select more than one answer.
However, the experimentalArray only shows the last answer chosen in both drink and cookie question.
For example, I choose Oreos and Chocolate Chip in cookies, and Soda and Wine in drinks.
The answer comes out as: "Cookie - Chocolate chip Drink - Wine"
Why is it not displaying all values?
Edited for a multi-dimensional script
<?php
$drinks = $_POST['drinks'];
$cookies = $_POST['cookies'];
$combinedArray = array( 'Cookies' => $cookies, 'Drinks' => $drinks);
foreach($combinedArray as $snackType => $snack) {
print "<h2>$snackType</h2>";
foreach ($snack as $number => $snackChosen) {
print " $number is $snackChosen<br />";
}
}
?>
Ok, so tried to do a multi-dimensional array script instead since the previous script wasn't going to obtain all the values as per the HTML form.
This script works (was ripped off from a book and modified for this code here), however, $number value starts at 0. How do I modify that so that it starts at 1 instead?
Also, is this proper form for doing multi-dimensional array? Could it have been rewritten in a better way?
And again, thank you for all responses. Even if I don't quite understand them! :) So, thank you for your patience as well.

No idea which php tutorial you are using, but stop using it, it's probably horribly outdated!
You'll have to use the $_POST / $_GET / $_REQUEST (contains POST, GET and - depending on the config - Cookie values) arrays. register_globals is deprecated and a potential security hole if enabled.
For example, use $drinks = isset($_POST['drinks']) ? $_POST['drinks'] : array(); to get your $drinks array. Same for the $cookies one.
About your array issue. It looks like you want the keys from the drinks array and the values from hte cookies array. If yes, have a look at array_combine(). Note that it requires both arrays to have the same amount of elements though so feeding it with user-generated arrays where the length can vary is not a very good idea.
Fyi, $experimentalArray = array($cookie => $d); maps the last element from $cookies to the last element of $drinks because PHP has no block scope and thus $cookie and $d still point to the last array elements you got in your foreach loops.

There are a few problems here.
When you iterate with foreach and you would like to perform actions for each element, the code need to be between the foreach's brakets.
There is always a chance that the user will select a different number of cookies than drinks, which would cause problems because one food item wouldn't have a pair.
The solution that I propose is the array_combine() function to pair cookies with drinks after padding each of the arrays to the same length:
<?php
//$cookies = array_pad( $cookies , count( $drinks ), '(none)' ); Doesn't work, thanks for pointing this out #Phoenix
$drinks = array_pad( $drinks, count( $cookies ), '(none)' ); //If more cookies than drinks, add (none) drinks to account for extra cookies
$combined = array_combine( $cookies, $drinks ); //Combine arrays and cookies
?>
Of course this may seem a little complicated so let me explain. Let's say the user choses Sugar and Chocolate Chip Cookies with Milk and Water. These obviously aren't the tastiest choices, but they are the most optimal in this case. Since there are an equal number of cookies as there are drinks, PHP will simply pair up the choices with the array_combine() function. This function accepts an array of keys (cookies) and values ($drinks) and combines them into one. Ex.
array_combine( array( 'one', 'two' ), array( 1, 2 ) ) == array( 'one' => 1, 'two' => 2 );
We run into problems when the user chooses an unequal number of favorite snacks. This is when the first two lines come into play. The first line will add (none) to the cookie array for each extra drink. Ex.
array_pad( array( 'one' ), 2, '(none)' ) == array( 'one', '(none)' );
The next line does the same, but instead equals the drinks with the cookies (in the case there are more cookies selected than drinks).
Let me know if you need more examples.

What about the previous two? Do they work appropriately? I believe that the problem is here:
$experimentalArray = array($cookie => $d);
Which should be
$experimentalArray = array($cookies => $drinks);
I don't think I've ever made an array in that fashion though, not sure if the values of $cookies will automatically be the keys of $drinks.
If not, a method of generating such an array would be something like:
if(count($drinks) >= count($cookies))
{
$count = count($drinks);
}
else
{
$count = count($cookies);
}
for($i=1;$i<=$count;$i++)
{
if(isset($drinks[$i]) && isset($cookies[$i]))
{
$experimentalArray[$cookies[$i]] = $drinks[$i];
}
else if(isset($drinks[$i]) && !isset($cookies[$i]))
{
$experimentalArray['None' . $i] = $drinks[$i];
}
else
{
$experimentalArray[$cookies[$i]] = 'None' . $i;
}
}
Or something like that, which accounts for if there are fewer of one type chosen than the other.

This script works (was ripped off from a book and modified for this code here), however, $number value starts at 0. How do I modify that so that it starts at 1 instead?
PHP always starts the Count at 0.
You can read more about that at:
http://php.net/manual/de/language.types.array.php

Related

PHP variable variables in a foreach loop aren't working

This is a brilliant little trick if I can get it to work - I have hundreds data columns from dozens of tables spread across a dozen data forms (they are HTML print forms) and they are all html with embedded php variables. Very normal. However the customer had a requirement to know what field went in where - a very good question.
So what did I do? I worked on a solution that allows the key'd arrays from the database to give up their column names. a brilliant move! except I need to do it via variable variables, and guess what, they DON'T work in a foreach loop.
here is the code
if ($_REQUEST['data']=="false"){
$supera = array("RowService", "RowSite", "RowCustomer", "RowEngineer"); //there can be many of these they are key'd arrays $RowService['column_name_1']; is the format
foreach($supera as $super){
foreach(${$super} as $key=>$value){
if (!is_numeric($key)){
${$super}[$key] = "<span style=\"color:pink;\">".$key."</span>";
}
}
}
}
as you can see I want a kill switch easy mechanism to cut and paste the key'd arrays that aren't to show real data any more rather they are to show (in pink) the column name, and (perhaps) the table name too. There is a lot of code already in place and this would be a brilliant option if it can be made to work
EDIT: this is the PHP error:
Warning: Invalid argument supplied for foreach()
EDIT: THE CODE ACTUALLY ALREADY WORKS: FIX IS TO test for is_array()
if(is_array(${$super})) foreach(${$super} as $key=>$value){
will work, as opposed to just
foreach(${$super} as $key=>$value){
I'm not sure what you are trying to achieve but your code (simplified) works just fine:
$a = array("asd", "qwe");
$asd = array("a" => 1, "b" => 2, "c" => 3);
$qwe = array("d" => 4, "e" => 5, "f" => 6);
foreach ($a as $item)
{
foreach ($$item as $key => $value)
{
echo $key . ": " . $value . "<br />";
}
}
Output:
a: 1
b: 2
c: 3
d: 4
e: 5
f: 6
Most likely one of your variables is empty (not an array) and that's why you receive that warning.
Personally, I find variable variables to be a really bad idea. There are a few ways around it.
For example:
$process = array(&$RowService,&$RowSite,&$RowCustomer,&$RowEngineer);
foreach($process as $p) {
foreach($p as $k=>$v) {
$p[$k] = "<span style=\"color:pink\">".$v."</span>";
}
}
Using references means you can affect the original variables.
If the above doesn't work (I'm not that great with references XD), try this:
$process = array($RowService,$RowSite,$RowCustomer,$RowEngineer);
foreach($process as $p) {
foreach($p as $k=>$v) {
$p[$k] = "<span style=\"color:pink\">".$v."</span>";
}
}
list($RowService,$RowSite,$RowCustomer,$RowEngineer) = $process;
As per my understanding of your requirement.
If you want to get table name with pink color then you just need to use below code
$supera = array("RowService", "RowSite", "RowCustomer", "RowEngineer"); //there can be many of these they are key'd arrays $RowService['column_name_1']; is the format
$super = array();
foreach($supera as $key=>$value){
if (!is_numeric($value)){
$super[$value] = "<span style=\"color:pink;\">".$value."</span>";
}
}
print_r($super);

How to send array with various submit buttons?

Can you send an array with various values in html? I would like to send different array values with various different submit buttons all within one <form> element.
Here is what I am doing currently. It works so I'm not having a problem, but I couldn't find any documentation for anything similar and I am really curious if theres another way.
Button with my *psuedo*array
<input type="submit" name="form_action" value="action:new_business,id:0">
Decode function:
$action = explode(',', $_POST['form_action']);
$new = array();
foreach ($action as $v) {
$t = explode(':',$v);
$new[$t[0]] = $t[1];
}
print_r($new);
And the results:
Array ( [action] => new_business [id] => 0 )
Of course, this works, so I'm really just curious whether there's a built in solution already.
The desired simplicity:
<input type="submit" name="array" value="array('0'=>'foo','1'=>'bar')">
print_r($_POST['array]);
Array ( [0] => foo [1] => bar )
Edit: I know how to send arrays with html, but that was not my question. If I use hidden inputs, they get sent regardless of which submit button I click, there will be multiple submit buttons contained in one <form> element, and I need to know which was clicked and what action it is going to be used for. Sorry if that was unclear but I don't think I deserve downvotes either way...
Try this:
<input type="hidden" name="form_action[action]" value="new_business" />
<input type="hidden" name="form_action[id]" value="0" />
Inputs with names of the form name[key] will be condensed into an array. This also applies to name[], which will become elements of an indexed array.
I know the question is old. I still like to answer this, as I am implementing currently something similar.
You can indeed write your statement and turn it into a valid context. By implementing:
if(isset($_POST['array'])){
eval('$my = '.$_POST['array'].';');
print_r($my);
}
you concatenate your POST to a valid php expression in string format and evaluate it. This however is very dangerous because it allows execution of any code inside without any verification. You MUST NOT use this in a public environment because anyone can easily modify the string being send by the button.
The best solution in terms of safety and efficiency is really to send a csv format:
<input type="submit" name="array" value="array('0','foo','1','bar')">
in php do:
$my_assoc = array();
if(isset($_POST['action'])){
$my = explode(",",filter_input(INPUT_POST, 'action' , FILTER_SANITIZE_STRING));
for($i = 0, $num = count($my); $i < $num; $i+=2){
$my_assoc[$my[$i]] = $my[$i+1];
}
print_r($my_assoc);
}
The explode function is linear in complexity and has no large impact. By filtering the csv string, you can also make sure to have no unwanted characters in it (never trust incoming data). You can then either keep the indexed array ($my) and treat every two values as (psydo) key-value pair or turn it into an associative array ($my_assoc).

Improving algorithm using MySQL

The code below is written mainly using PHP, but I am hoping to speed up the process, and parsing strings in PHP is slow.
Assume the following where I get a string from the database, and converted it into an array.
$data['options_list'] = array(
"Colours" => array('red','blue','green','purple'),
"Length" => array('3','4','5','6'),
"Voltage" => array('6v','12v','15v'),
);
These subarrays will each be a dropdown Select list, an the end user can select exactly 1 from each of the select lists.
When the user hits submit, I will want to match the submitted values against a "price table" pre-defined by the admins. Potentially "red" and "6v" would cost $5, but "red" and "5"(length) and "6v" would cost $6.
The question is, how to do so?
Currently the approach I have taken is such:
Upon submission of the form (of the 3 select lists), I get the relevant price rules set by the admin from the database. I've made an example of results.
$data['price_table'] =
array(
'red;4'=>'2',
'red;5'=>'3',
'red;6'=>'4',
'blue;3'=>'5',
'blue;4'=>'6',
'blue;5'=>'7',
'blue;6'=>'8',
'green;3'=>'9',
'green;4'=>'10',
'green;5'=>'11',
'green;6'=>'12',
'purple;3'=>'13',
'purple;4'=>'14',
'purple;5'=>'15',
'purple;6'=>'16',
'red;3'=>'1',
'red;3;12v'=>'17',
'blue;6;15v'=>'18',
);
Note : The order of the above example can be of any order, and the algorithm should work.
I then explode each of the above elements into an array, and gets the result that matches the best score.
$option_choices = $this->input->post('select');
$score = 0;
foreach($data['price_table'] as $key=>$value)
{
$temp = 0;
$keys = explode(';',$key);
foreach($keys as $k)
{
if(in_array($k, $option_choices))
{
$temp++;
}else{
$temp--;
}
}
if($temp > $score)
{
$score = $temp;
$result = $value;
}
}
echo "Result : ".$result;
Examples of expected results:
Selected options: "red","5"
Result: 3
Selected Options: "3", "red"
Result: 1
Selected Options: "red", "3", "12v"
Result: 17
The current method works as expected. However, handling these using PHP is slow. I've thought of using JSON, but that would mean that I would be giving the users my whole price table, which isn't really what I am looking for. I have also thought of using another language, (e.g python) but it wouldn't particularly be practical considering the costs. That leaves me with MySQL.
If someone can suggest a cheap and cost-efficient way to do this, please provide and example. Better still if you could provide an even better PHP solution to this which works fast.
Thank you!
It looks like you did work to make the results read faster but you're still parsing and testing every array part against the full list? This would probably run faster moving the search to MySQL and having extra columns there.
Since you can control the array (or test string) perhaps try fixed length strings:
$results = explode("\n", "
1 Red v1
22 Blue v2
333 Green v3");
$i = 0;
while($i < count($results)) {
$a = substr($results[$i], 0, 10);
$b = substr($results[$i], 10, 20);
$c = substr($results[$i], strpos(' ', strrev($results[$i]))-1);
if(stripos($userInput, $a . $b . $c) !== false) {
// parse more...
Supposedly JavaScript is good at this with memoizaion:
http://addyosmani.com/blog/faster-javascript-memoization/

How do I echo each value of a foreach loop?

Not sure if the question title actually makes sense.
Anyway, what I'd like to do is be able to echo an individual value from a foreach loop.
Here's my code:
$headlines = array('primary_headline' => $_POST['primary_headline'], 'secondary_headline' => $_POST['secondary_headline'], 'primary_subline' => $_POST['primary_subline'], 'secondary_subtext' => $_POST['secondary_subtext']);
$city_name = "Dallas";
$ref_name = "Facebook";
$searches = array('$city_name', '$ref_name');
$replacements = array($city_name, $ref_name);
if(isset($headlines)) {
foreach($headlines as $headline) {
$headline = str_replace($searches, $replacements, $headline);
echo($headline['primary_headline']); // I thought this would do the trick
}
}
I thought that this would've echoed my city is Dallas when my city is $city_name was posted, unfortunately, this isn't the case and it merely echoes msps, which is the first letter of each input value:
<input name="primary_headline" type="text" value="my city is $city_name" />
<input name="secondary_headline" type="text" value="secondary headline" />
<input name="primary_subline" type="text" value="primary subline" />
<input name="secondary_subtext" type="text" value="secondary subline" />
<input type="submit" value="submit" />
If anyone could point me in the right direction, it would be very much appreciated!! :)
$searches = array('$city_name', '$ref_name');
The single quotes are making $searches literally contain the word $city_name, not the VALUE of $city_name. You don't need quotes while assigning variables:
$searches = array($city_name, $ref_name);
unless, of course, you're doing some kind of templating system and trying to do variable interpolation without eval().
Change
echo($headline['primary_headline']); // I thought this would do the trick
To
echo($headline) . PHP_EOL; // I thought this would do the trick
When you are using foreach you do not need to specify an index to the element, because foreach will handle iterating for you, so when you dereference something inside the loop, you are asking for a character from the string. Here you get the first character because 'primary_headline' is being interpreted as a 0.
$headlines = array('primary_headline' => $_POST['primary_headline'], 'secondary_headline' => $_POST['secondary_headline'], 'primary_subline' => $_POST['primary_subline'], 'secondary_subtext' => $_POST['secondary_subtext']);
This creates an array with key=>value pairs, not a multidimensional array. Looping through this array in a foreach loop will return only the values, i.e. $_POST['primary_headline'] for the first iteration, $_POST['secondary_headline'] for the second iteration, etc. This is why you're unable to access $headline['primary_headline'].
If you want to access "my city is Dallas" per your example, simply echo $headlines['primary_headline'].
If you want to echo each value:
foreach($headlines as $headline) {
echo $headline . PHP_EOL;
}
For good measure figured I should close this question with an answer (I've already placed it in the comments section as I couldn't add an answer).
The ampersand (&) must be used in order to assign a reference to the original array $headlines instead of copying it's valuables, thus updating it with any values created within the foreach() loop.
So, my code is now;
$headlines = array('primary_headline' => $_POST['primary_headline'], 'secondary_headline' => $_POST['secondary_headline'], 'primary_subline' => $_POST['primary_subline'], 'secondary_subtext' => $_POST['secondary_subtext']);
$city_name = "Dallas";
$ref_name = "Facebook";
$searches = array('$city_name', '$ref_name');
$replacements = array($city_name, $ref_name);
if(isset($headlines)) {
foreach($headlines as &$headline) {
$headline = str_replace($searches, $replacements, $headline);
}
echo ($headlines['primary_headline']) // This now has the value of $_POST['primary_headline'] along with the str_replace() function assigned to $headline
}

How to process a large post array in PHP where item names are all different and not known in advance?

I have a PHP page that queries a DB to populate a form for the user to modify the data and submit.
The query returns a number of rows which contain 3 items:
ImageID
ImageName
ImageDescription
The PHP page titles each box in the form with a generic name and appends the ImageID to it. Ie:
ImageID_03
ImageName_34
ImageDescription_22
As it's unknown which images are going to have been retrieved from the DB then I can't know in advance what the name of the form entries will be.
The form deals with a large number of entries at the same time.
My backend PHP form processor that gets the data just sees it as one big array:
[imageid_2] => 2
[imagename_2] => _MG_0214
[imageid_10] => 10
[imagename_10] => _MG_0419
[imageid_39] => 39
[imagename_39] => _MG_0420
[imageid_22] => 22
[imagename_22] => Curly Fern
[imagedescription_2] => Wibble
[imagedescription_10] => Wobble
[imagedescription_39] => Fred
[imagedescription_22] => Sally
I've tried to do an array walk on it to split it into 3 arrays which set places but am stuck:
// define empty arrays
$imageidarray = array();
$imagenamearray = array();
$imagedescriptionarray = array();
// our function to call when we walk through the posted items array
function assignvars($entry, $key)
{
if (preg_match("/imageid/i", $key)) {
array_push($imageidarray, $entry);
} elseif (preg_match("/imagename/i", $key)) {
// echo " ImageName: $entry";
} elseif (preg_match("/imagedescription/i", $key)) {
// echo " ImageDescription: $entry";
}
}
array_walk($_POST, 'assignvars');
This fails with the error:
array_push(): First argument should be an array in...
Am I approaching this wrong?
Would it be possible to change the way the items are named on the form?
Current:
ImageID_03
ImageName_34
ImageDescription_22
Changed To:
ImageID[03]
ImageName[34]
ImageDescription[22]
This way it should come through the $_POST as three separate arrays meaning you can skip all that extra processing.
I hate to edit many rows at once. It's usability fault
If I go for it, I'd make it with such a form:
<form method="POST">
<input type="text" name="name[1]">
<input type="text" name="desc[1]">
<input type="text" name="name[2]">
<input type="text" name="desc[2]">
...etc
</form>
So, I'd have 2 arrays, $_POST['name'] and $_POST['desc'] where id used as a key
In your case I wouldn't use array_walk as it's just a syntax sugar, and make it with foreach to have full contorol over data processing.
I think you need
global $imageidarray;
to access a global array inside a function.
Something like this would work as well, assuming your three form fields are always submitted together:
$ids = preg_grep('/^imageid_(\d+)$/', array_keys($_POST)); // $ids now contains only the imageid_* keys
foreach($ids as $id) {
$id = substr($ids, 8); // now contains the numeric id of the field
echo $id, ": ", $_POST["imageid_$id"], "\n";
}
Not as efficient as using the array notation in the form names, but allows you to trivially extract just the ID numbers from one of the field names and then use it to access the other matching fields as well.

Categories