I have a POST request coming to one of my pages, here is a small segment:
[shipCountry] => United States
[status] => Accepted
[sku1] => test
[product1] => Test Product
[quantity1] => 1
[price1] => 0.00
This request can be any size, and each products name and quantity's key would come across as "productN" and "quantityN", where N is an integer, starting from 1.
I would like to be able to count how many unique keys match the format above, which would give me a count of how many products were ordered (a number which is not explicitly given in the request).
What's the best way to do this in PHP?
Well, if you know that every product will have a corresponding array key matching "productN", you could do this:
$productKeyCount = count(preg_grep("/^product(\d)+$/",array_keys($_POST)));
preg_grep() works well on arrays for that kind of thing.
What Gumbo meant with his "use array instead" comment is the following:
In your HTML-form use this:
<input type="text" name="quantity[]" />
and $_POST['quantity'] will then be an array of all containing all of your quantities.
If you need to supply an id you can also do this:
<input type="text" name="quantity[0]" />
$_POST['quantity][0] will then hold the corresponding quantity.
As mentioned by gumbo you could group all parameters describing one item in its own array which usually makes it easier to iterate them. You may not have control over the POST parameters but you can restructure them like e.g. with
<?php
$testdata = array(
'shipCountry' => 'United States',
'status' => 'Accepted',
'sku1' => 'test1',
'product1' => 'Test Product1',
'quantity1' => '1',
'price1' => '0.01',
'sku2' => 'test2',
'product2' => 'Test Product2',
'quantity2' => '2',
'price2' => '0.02'
);
$pattern = '/^(.*\D)(\d+)$/';
$foo = array('items'=>array());
foreach($testdata as $k=>$v) {
if ( preg_match($pattern, $k, $m) ) {
$foo['items'][$m[2]][$m[1]] = $v;
}
else {
$foo[$k] = $v;
}
}
print_r($foo);
Though there be plenty of examples, if you're guaranteed that the numbers should be contiguous, I usually take the approach:
<?php
$i = 1;
while( isset($_POST['product'.$i) )
{
// do something
$i++;
}
Related
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;
}
Good Afternoon.
I'm attempting to make an array list with one of the key values being ($_REQUEST['qty#']), where "#" would be the current number of the item within the array (as it pertains to a field in a form that gathers this info).
For example:
$itemdetails = array(
array(
'qty' => ($_REQUEST['qty1']),
'price' => 0.70,
'pn' => 'TV-1000',
array(
'qty' => ($_REQUEST['qty2']),
'price' => 0.99,
'pn' => 'TV-5000'));
Is there any way that I can automatically have the number in ($_REQUEST['qty']) be determined without having to type in the numbers manually?
Just wondering. My next guess would be to enter it all into a database and pull it from there.
Thanks a bunch in advance.
You would need to loop ...
$itemdetails = array(
array(
'price' => 0.70,
'pn' => 'TV-1000'
),
array(
'price' => 0.99,
'pn' => 'TV-5000'
)
);
foreach ( $itemdetails as $k => &$item ) {
$item['qty'] = $k + 1;
}
What you should do is pass an array of data via POST/GET.
To do this in your inputs, you make the name value = qty[] for each input. Note the array syntax [] here.
PHP will automatically take all values for input with that array syntax and build an array out of it in $_POST/$_REQUEST.
So you would be able to access your array like
var_dump($_POST['qty']);
var_dump($_REQUEST['qty']);
That however still doesn't give the ability to match this to the price/pn as you need. So, let's take the array syntax one step further and actually put a key value in it like this:
<input name="qty[0]" ... />
<input name="qty[1]" ... />
By doing this you will be able to know exactly which array index matches which item (assuming you know the order the inputs were displayed in).
The would make $_POST['qty'][0] be the first item, $_POST['qty'][1] be the next and so on.
So assuming you also have you prices/pn in an array like this:
$item_array = array(
0 => array('price' => 123, 'pn' = 'ABC'),
1 => array('price' => 456, 'pn' = 'XYZ'),
...
);
You could then easily loop through the input quantities and build you final array like this:
$itemdetails = array();
foreach ($_REQUEST['qty'] as $key => $value) {
$itemdetails[$key] = $item_array[$key];
$itemdetails[$key]['qty'] = $value;
)
Also note, that if you are expecting this data to be passed via POST, it is considered best practice to use the $_POST superglobal rather than the $_REQUEST superglobal.
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'];.
I would like to retrieve the first key from this multi-dimensional array.
Array
(
[User] => Array
(
[id] => 2
[firstname] => first
[lastname] => last
[phone] => 123-1456
[email] =>
[website] =>
[group_id] => 1
[company_id] => 1
)
)
This array is stored in $this->data.
Right now I am using key($this->data) which retrieves 'User' as it should but this doesn't feel like the correct way to reach the result.
Are there any other ways to retrieve this result?
Thanks
There are other ways of doing it but nothing as quick and as short as using key(). Every other usage is for getting all keys. For example, all of these will return the first key in an array:
$keys=array_keys($this->data);
echo $keys[0]; //prints first key
foreach ($this->data as $key => $value)
{
echo $key;
break;
}
As you can see both are sloppy.
If you want a oneliner, but you want to protect yourself from accidentally getting the wrong key if the iterator is not on the first element, try this:
reset($this->data);
reset():
reset() rewinds array 's internal
pointer to the first element and
returns the value of the first array
element.
But what you're doing looks fine to me. There is a function that does exactly what you want in one line; what else could you want?
Use this (PHP 5.5+):
echo reset(array_column($this->data, 'id'));
I had a similar problem to solve and was pleased to find this post. However, the solutions provided only works for 2 levels and do not work for a multi-dimensional array with any number of levels. I needed a solution that could work for an array with any dimension and could find the first keys of each level.
After a bit of work I found a solution that may be useful to someone else and therefore I included my solution as part of this post.
Here is a sample start array:
$myArray = array(
'referrer' => array(
'week' => array(
'201901' => array(
'Internal' => array(
'page' => array(
'number' => 201,
'visits' => 5
)
),
'External' => array(
'page' => array(
'number' => 121,
'visits' => 1
)
),
),
'201902' => array(
'Social' => array(
'page' => array(
'number' => 921,
'visits' => 100
)
),
'External' => array(
'page' => array(
'number' => 88,
'visits' => 4
)
),
)
)
)
);
As this function needs to display all the fist keys whatever the dimension of the array, this suggested a recursive function and my function looks like this:
function getFirstKeys($arr){
$keys = '';
reset($arr);
$key = key($arr);
$arr1 = $arr[$key];
if (is_array($arr1)){
$keys .= $key . '|'. getFirstKeys($arr1);
} else {
$keys = $key;
}
return $keys;
}
When the function is called using the code:
$xx = getFirstKeys($myArray);
echo '<h4>Get First Keys</h4>';
echo '<li>The keys are: '.$xx.'</li>';
the output is:
Get First Keys
The keys are: referrer|week|201901|Internal|page|number
I hope this saves someone a bit of time should they encounter a similar problem.
This is a php example, but an algorithm for any language would do. What I specifically want to do is bubble up the United States and Canada to the top of the list. Here is an example of the array shortened for brevity.
array(
0 => '-- SELECT --',
1 => 'Afghanistan',
2 => 'Albania',
3 => 'Algeria',
4 => 'American Samoa',
5 => 'Andorra',)
The id's need to stay intact. So making them -1 or -2 will unfortunately not work.
What I usually do in these situations is to add a separate field called DisplayOrder or something similar. Everything defaults to, say, 1... You then sort by DisplayOrder and then the Name. If you want something higher or lower on the list, you can tweak the display order accordingly while keeping your normal IDs as-is.
-- Kevin Fairchild
My shortcut in similar cases is to add a space at the start of Canada and two spaces at the start of United States. If displaying these as options in a SELECT tag, the spaces are not visible but the sorting still brings them to the front.
However, that may be a little hacky in some contexts. In Java the thing to do would be to extend StringComparator, override the compare() method making the US and Canada special cases, then sort the list (or array) passing in your new comparator as the sort algorithm.
However I would imagine it might be simpler to just find the relevant entries in the array, remove them from the array and add them again at the start. If you are in some kind of framework which will re-sort the array, then it might not work. But in most cases that will do just fine.
[edit] I see that you are using a hash and not an array - so it will depend on how you are doing the sorting. Could you simply put the US into the hash with a key of -2, Canada with -1 and then sort by ID instead? Not having used PHP in anger for 11 years, I don't recall whether it has built-in sorting in its hashes or if you are doing that at the application level.
$a = array(
0 => '- select -',
1 => 'Afghanistan',
2 => 'Albania',
3 => 'Algeria',
80 => 'USA'
);
$temp = array();
foreach ($a as $k => $v) {
$v == 'USA'
? array_unshift($temp, array($k, $v))
: array_push($temp, array($k, $v));
}
foreach ($temp as $t) {
list ($k, $v) = $t;
echo "$k => $v\n";
}
The output is then:
80 => USA
0 => - select -
1 => Afghanistan
2 => Albania
3 => Algeria
You can not change the order of elements within the same array by "moving" an item around. What you can do it to build a new array that first has your favourite items and then adds anything else from the original countries array at the end:
$countries = array(
0 => '-- SELECT --',
1 => 'Afghanistan',
2 => 'Albania',
3 => 'Algeria',
4 => 'American Samoa',
5 => 'Andorra',
22 => 'Canada',
44 => 'United States',);
# tell what should be upfront (by id)
$favourites = array(0, 44, 22);
# add favourites at first
$ordered = array();
foreach($favourites as $id)
{
$ordered[$id] = $countries[$id];
}
# add everything else
$ordered += array_diff_assoc($countries, $ordered);
# result
print_r($ordered);
Demo
It's been ages since I don't know how to code. But yes.
array_unshift($queue, "United States", "Canada");
print_r($queue);
array_unshift — Prepend one or more elements to the beginning of an array