I have a multidimensional array which looks like this:
Array
(
[0] => Array
(
[0] => Array
(
[description] => UPS Ground
[delivery-time] => 1-5 business days
[shipping-amount] => 1299
)
[1] => Array
(
[description] => UPS 3 Day Select
[delivery-time] => 3 business days
[shipping-amount] => 2459
)
[2] => Array
(
[description] => UPS 2nd Day Air
[delivery-time] => 2 business days
[shipping-amount] => 3239
)
)
[1] => Array
(
[0] => Array
(
[description] => UPS Ground
[delivery-time] => 1-5 business days
[shipping-amount] => 864
)
[1] => Array
(
[description] => UPS 3 Day Select
[delivery-time] => 3 business days
[shipping-amount] => 1109
)
[2] => Array
(
[description] => UPS 2nd Day Air
[delivery-time] => 2 business days
[shipping-amount] => 1633
)
[3] => Array
(
[description] => UPS Overnight
[delivery-time] => 1 business day
[shipping-amount] => 3528
)
)
)
I'm trying to achieve 3 things:
Add the values of the shipping-amount where the description is the same across dimensions
Drop the array if it contains a description which doesn't exist in every other dimension
Drop a dimension once the shipping-amounts are combined
There may be several first-level arrays (not just 2 as shown here), but this is as deep as the dimensions will go. I'm looking for the following result:
Array
(
[0] => Array
(
[description] => UPS Ground
[delivery-time] => 1-5 business days
[shipping-amount] => 2163
)
[1] => Array
(
[description] => UPS 3 Day Select
[delivery-time] => 3 business days
[shipping-amount] => 3568
)
[2] => Array
(
[description] => UPS 2nd Day Air
[delivery-time] => 2 business days
[shipping-amount] => 4872
)
)
Thanks in advance!
I think this would work:
$final=array(); // the final array
$count=array(); // keeps track of instances of each description
$loops=count($array);
for($a=0;$a<$loops;$a++){
foreach($array[$a] as $s){ //loop through child arrays
if($count[$s['description']]>0){ //check if description exists in $count
foreach($final as $k=>$v){ //add sums to the final if it does exist
if($final[$k]['description']==$s['description']){$final[$k]['shipping-amount']+=$s['shipping-amount'];}
}
}else{ //if it doesn't exist in the count array, add it to the final array
$final[]=$s;
}
$count[$s['description']]++;//update the count array
}
}
//Unset singletons, using the count array
foreach($count as $k=>$v){
if($v==1){
foreach($final as $key=>$val){
if($final[$key]['description']==$k){unset($final[$key]);}
}
}
}
print_r($final);
I have been stuck on an issue for the past 2 days and feel you, so I hope this helps.
I'm not going to write code because I agree with deceze.
Nonetheless, my recommendation would be a custom function that:
loops over an input array
applies the logic you outlined
return the condensed array
Given your specific requirements isn't a single, magical PHP function that does this. Yet, there are dozens of PHP array functions you could utilize within your custom function.
If you need help on a particular piece, update your post or ask a new question.
I would recommend using the array_walk function with a callback that handles your specific requirements for addition and uniqueness.
Related
I am working on a chat.
I have an array that contains the info regarding the room
(in the example below, 2 rooms (the lounge, the beach)).
I have an issue when I have an empty room (in the example the beach), as it contains by default no user, (which is a Null user).
$roomNusers=Array (
[The Lounge] =>
Array ( [id] => 1
[privacy] => public
[users] => Array
[QUICHE POIREAU] => Array
[smiley] => smi_smile.gif
[name] => QUICHE POIREAU
[state] => NULL
[id] => 1 )
[JOHN DOE] => Array
[smiley] => smi_smile.gif
[name] => Joe FRANC
[state] =>
[id] => 40 )
[The Beach] => Array
[id] => 2
[privacy] => public
[users] => Array
[Null] => Array
[smiley] => Null
[name] => Null
[state] => Null
[id] => Null
I am trying to count, in my array, the number of users currently present in the room.
Looking around Stack Overflow, I managed to find the solution I wanted:
foreach($roomNusers as $room => $value)
{
echo $room.' user count:'.count($room['users'])
}
This output:
The lounge user count: 2
The beach user count: 1
My issue is that it counts the user [null] in the beach.
How can I count the number of users not null per room?
I thought of a workaround with something similar to:
$countperroom= .count($room['users'])-1;
if(isset(end([$room]['users']))){$countuser+1;}
In this, the last user is empty, I do not add an user, but I do not know how to write this.
Rather than counting the number of values in $room['users'], you could count the number of keys after filtering them to remove empty keys:
foreach ($rooms as $name => $room) {
$users = count(array_filter(array_keys($room['users'])));
echo "$name: $users users\n";
}
Output (for your sample data):
The Lounge: 2 users
The Beach: 0 users
Demo on 3v4l.org
I have a two dimensional array and wish to always delete/unset the last array item (in this case Array[3]) in the code sample below, before I put it into a SESSION.
I am still a novice with php and have tried the following with no success.
Any help would be greatly appreciated.
if (is_array$shoppingCartContents)) {
foreach($shoppingCartContents as $k=>$v) {
if($v[1] === 999999) {
unset($shoppingCartContents[$k]);
}
}
}
$shoppingCartContents = Array
(
[0] => Array
(
[productId] => 27
[productTitle] => Saffron, Dill & Mustard Mayonnaise
[price] => 6.50
[quantity] => 3
)
[1] => Array
(
[productId] => 28
[productTitle] => Wasabi Mayonnaise
[price] => 6.50
[quantity] => 3
)
[2] => Array
(
[productId] => 29
[productTitle] => Chilli Mayo
[price] => 6.50
[quantity] => 2
)
[3] => Array
(
[productId] => 999999
[productTitle] => Postage
[price] => 8.50
[quantity] => 1
)
)
Just use array_pop()
$last_array_element = array_pop($shoppingCartContents);
// $shoppingCartContents now has last item removed
So in your code:
if (is_array($shoppingCartContents)) {
array_pop($shoppingCartContents); // you don't care about last items, so no need to keep it's value in memory
}
Your code will fail as you're using strings for keys, not numbers, so the comparison
if($v[1] === 999999)
will never match, and should be checking $v['productId'].
For your use case, rather than looping through the array, you can just pop the last item off:
array_pop($shoppingCartContents);
array_pop removes the last item from an array. It returns that last item, but since you don't want to keep the last item, we're not saving the return value.
Alternatively, if you still wanted to use unset, you could get the last key, and then unset using that.
Finally, as it looks like you've got a true list (i.e. consecutive, numerical indices), you could get away with something like unset($shoppingCartContents[count($shoppingCartContents)-1]);
All that being said, array_pop is the way to go.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I sort a multidimensional array in php
........
This is not a duplicate. There are indeed tons of questions on stackoverflow regarding sorting multi-dimensional arrays (I believe I read most of them yesterday, before posting my question...), but none that I have seen refer to my particular problem: sorting by a varriying number of fields. The answer given by #dev-null-dweller below does just this.
........
I'm trying to create a flexible sort function for a multi-dimensional array in PHP.
The flexibility I'm after is for the number of sorting fields (could be 1, 2, or 3 for a 1st, 2nd or 3rd level sort), and for the identity of those fields.
This is a sample bit of my array:
Array
(
[0] => Array
(
[title] => Australia Book
[price] => 209.00
[code] => 3571313
[con] => Australia & Oceania
[ctry] => Australia
)
[1] => Array
(
[title] => New Zealand Book
[price] => 169.00
[code] => 3571346
[con] => Australia & Oceania
[ctry] => New Zealand
)
[2] => Array
(
[title] => Austria Book
[price] => 129.00
[code] => 3571230
[con] => Europe
[ctry] => Austria
)
[3] => Array
(
[title] => Austria Pocket Book
[price] => 119.00
[code] => 3571354
[con] => Europe
[ctry] => Austria
)
[4] => Array
(
[title] => Amsterdam Book
[price] => 59.00
[code] => 3571350
[con] => Europe
[ctry] => Netherlands
)
[5] => Array
(
[title] => Britain
[price] => 59.00
[code] => 3571315
[con] => Europe
[ctry] => Britain
)
)
I sometimes need to sort only by title, sometimes by ctry then title, sometimes by con, then ctry then title, etc.
I've managed to make a 2 level sort like so:
usort($arrCatalog, array('Catalogs_model', 'multi_compare'));
function multi_compare($a ,$b) {
if ($a['con'] < $b['con']) {
return -1;
} elseif ($a['con'] > $b['con']) {
return 1;
} else {
return strcmp($a['title'], $b['title']);
}
}
The first question is how can I pass parameters to multi_compare function, when called from within usort.
After we settle that, I would be happy for some pointers as to how can I alter multi_sort so the identity and\or the number of fields will vary.
You are using static method for custom sorting, so you can set some static properties in this class before usort
class Catalogs_model{
public static $usort_criteria = array();
public static function multi_compare($a,$b){
foreach(self::$usort_criteria as $what => $order){
if($a[$what] == $b[$what]){
continue;
}
return (($order == 'desc')?-1:1) * strcmp($a[$what], $b[$what]);
}
return 0;
}
}
Catalogs_model::$usort_criteria = array(
'con' => 'asc',
'title' => 'asc'
);
usort($arrCatalog, array('Catalogs_model', 'multi_compare'));
of course it needs some tweaking , because now it only sorts strings.
I have an array of Towns that have no sorting whatsoever. I would like to sort by the [category][3] which is the province and then the [category][0] which is the region and display each Grouped Province with its regions and then towns underneath it. So the following array:
Array
(
[0] => Array
(
[name] => Name One
[id] => 1
[link] => http://mylink1.com
[category] => Array
(
[0] => Region 1
[1] => Town 7
[2] => Country
[3] => Province 2
)
)
[1] => Array
(
[name] => Name Two
[id] => 2
[link] => http://mylink2.com
[category] => Array
(
[0] => Region 1
[1] => Town
[2] => Country
[3] => Province 3
)
)
[2] => Array
(
[[name] => Name Three
[id] => 3
[link] => http://mylink3.com
[category] => Array
(
[0] => Region 1
[1] => Town 5
[2] => Country
[3] => Province 2
)
)
[6] => Array
(
[name] => Name Four
[id] => 4
[link] => http://mylink4.com
[category] => Array
(
[0] => Region 1
[1] => Town 1
[2] => Country
[3] => Province 1
)
)
)
... should end up looking like this:
Country (all the same)
Province 1
- Region 1
- - Town 1 name, id, link
Province 2
- Region 1
- - Town 5 name, id, link
- - Town 7 name, id, link
Province 3
- Region 1
- - Town 1 name, id, link
Province is the Primary Grouping factor, then sorted by Region, the Towns in no particular order but I guess Alphabetically would make sense.
I have managed to sort the array by Category using this reference: Sort Multi-dimensional Array by Value but cannot fathom how to sort any further without referencing the Province specifically in a loop by using its name. i.e.
/// shortened version..
foreach($array as $key=>$value)...
if($value == "Province 1") : do something here with these matches
... etc
Any help would be appreciated.
Take a look at the uasort() function:
http://www.php.net/manual/en/function.uasort.php
I don't think that you can do this in one step.
You can group your values like this:
$grouped = array();
foreach ($data as $group)
{
$grouped[ $group['category'][3] ][ $group['category'][0] ][ $group['category'][1] ] = array(/* ... */);
}
But you will still have to sort every array (and it's sub-arrays) using ksort().
You should check, whether you can get the values already presorted. If you, for example, are using a database to retrieve this values, it would be quite trivial to sort them in the database and bring them in the correct (grouped) structure in your PHP code.
looping through the array I've used a switch for the category I'm looking for and then built another array for each group. from there I can sort by region:
foreach($arr as $town){
switch($town['blogcats']){
case "Province 1" : $province1[] = $town;
break;
case...
etc
}
}
Then each new grouped array can be output:
foreach($province1 as $eachtown) {
echo $newtown['name'];
... and so forth.
}
Within the second loop sorting could be done on the Region.
Each Province is manually referenced in a list on my page which also gives me control over placement.
Howver, this answer could also be improved... but the above works without too much effort.
OK, you guys gave me a great solution for sorting a recordset array last Friday. (PHP 5.2 Function needed for GENERIC sorting of a recordset array)
But now when I implement it, I end up with an extra element in the recordset array. I won't wast space reposting the same info, as the link is above. But the bottom line is that when I sort an array of 5 records, the resulting array has 6 records. The last element in the array is not a record array, but rather just a element containing an integer value of 1. I presume that it is somehow getting the output value of the "strnatcasecmp" function, but I have no idea how it is happening.
Here is the function that you fine folks provided last week:
function getSortCommand($field, $sortfunc) {
return create_function('$var1, $var2', 'return '.$sortfunc.'($var1["'.$field.'"], $var2["' .$field .'"]);');
}
And here is the line I am calling to sort the array:
$trek[] = usort($trek, getSortCommand('name', 'strnatcasecmp'));
This produces the following output, with an extra element tacked on to the end.
Array
(
[0] => Array
(
[name] => Kirk
[shirt] => Gold
[assign] => Bridge
)
[1] => Array
(
[name] => McCoy
[shirt] => Blue
[assign] => Sick Bay
)
[2] => Array
(
[name] => Scotty
[shirt] => Red
[assign] => Engineering
)
[3] => Array
(
[name] => Spock
[shirt] => Blue
[assign] => Bridge
)
[4] => Array
(
[name] => Uhura
[shirt] => Red
[assign] => Bridge
)
[5] => 1
)
Just do
usort($trek, getSortCommand('name', 'strnatcasecmp'));
usort() returns a boolean indicating whether it was executed successfully (it sorts the elements in place):
Return Values
Returns TRUE on success or FALSE on failure.
By doing $trek[] = usort(...), you append the result of the function to the array.