Group and Sum this Array - php

I have an array that's output is this:
Array ( [winners] =>
Array ( [0] => Gold Member
[1] => CROTCH SNIFFER
[2] => TEAM #1 )
[prizeTotal] => 20 )
Array ( [winners] =>
Array ( [0] => TEAM #1
[1] => CROTCH SNIFFER )
[prizeTotal] => 60 )
Array ( [winners] =>
Array ( [0] => Gold Member
[1] => TEAM #1 )
[prizeTotal] => 30 )
Array ( [winners] =>
Array ( [0] => TEAM #1
[1] => TEAM #2
[2] => SCREW-NUT-BOLT )
[prizeTotal] => 90 )
Please forgive the names...it's not my DB.
I can not change the way the array is show here.
With that being said how can I group and sum?
1. For each winners array there is a prizeTotal below the team names. That prize total should be the value of each teamName above it.
Example
Array ( [winners] =>
Array ( [0] => Gold Member
[1] => CROTCH SNIFFER
[2] => TEAM #1 )
[prizeTotal] => 20 )
Gold Member should have 20
CROTCH SNIFFER should have 20
TEAM #1 should have 20 AND
Array ( [winners] =>
Array ( [0] => TEAM #1
[1] => CROTCH SNIFFER )
[prizeTotal] => 60 )
TEAM #1 should have 60
CROTCH SNIFFER sould have 60....etc....
Then I want to group by team name and sum so that I can display...
CROTCH SNIFFER = 80
Gold Member = 50
TEAM #1 = 200
Team #2 = 90.
Thanks in advance...

Assuming you can collect all your arrays into one i.e. :
$arrays = array($array1,$array2,....,$arrayn);
then
$grouping = array();
foreach($arrays AS $array)
{
foreach($array['winners'] AS $k=>$v)
{
//check if the array key is a number, will contain a team, and if that
//them is not alreay listed
if(is_numeric($k) && !array_key_exists($v,$grouping))
$grouping[$v] = 0;
//sum the prize to the team's sum
$grouping[$v] += intval($array['winners']['prizeTotal']);
}
}
//the following is just for debugging
print_r($grouping);
this should produce something like:
$grouping[TEAM #1] = 200
$grouping[TEAM #2] = 90
$grouping[CROTCH SNIFFER] = 200
...

You need to make a new array to hold your results, then for each of your existing arrays, check if you're already processed one for that team
If you have, then simply add the prizeTotal to the prizeTotal stored in your new array's entry for that team.
If not, simply add the team's entry to your new array.

I have made a function that imitates mysql SUM() and GROUP BY. I hope it will fit your needs:
$in_a = array(
array("a" => 0,"b"=>0,"s"=> 1),
array("a" => 0,"b"=>0,"s"=> 2),
array("a" => 1,"b"=>1,"s"=> 1),
array("a" => 0,"b"=>1,"s"=> 1),
array("a" => 0,"b"=>1,"s"=> 1),
array("a" => 1,"b"=>0,"s"=> 1),
array("a" => 0,"b"=>1,"s"=> 1),
array("a" => 1,"b"=>1,"s"=> 1),
array("a" => 1,"b"=>0,"s"=> 1),
);//input array exaple
$group_by_a = array("a","b");//input array will be grouped by these
$sum_a = array("s"); //'s' values of input will be summed
$out_a = array(); //this is the output array
foreach($in_a as $in_i => $in)
{
$add = false;
foreach($out_a as $out_i => $out)
{
$add = true;
foreach($group_by_a as $group_by)
if($in[$group_by] != $out[$group_by])
{
$add = false;
break;
}
if($add)
{
foreach($sum_a as $sum)
$out_a[$out_i][$sum] += $in[$sum];
break;
}
}
if(!$add)
{
foreach($group_by_a as $group_by)
$out_a[$in_i][$group_by] = $in[$group_by];
foreach($sum_a as $sum)
$out_a[$in_i][$sum] = $in[$sum];
}
}
the result:
Array
(
[0] => Array
(
[a] => 0
[b] => 0
[s] => 3
)
[2] => Array
(
[a] => 1
[b] => 1
[s] => 2
)
[3] => Array
(
[a] => 0
[b] => 1
[s] => 3
)
[5] => Array
(
[a] => 1
[b] => 0
[s] => 2
)
)

Related

Get array element with sub elements without repeating in PHP

I walk around here with some hesitation, I have passed an array with sub elements (so to speak) and I need three random values ​​but these are obtained without repeating.
The array is as follows:
Array
(
[0] => Array
(
[uid] => 1
[ticket_code] => 0oreb8yo
)
[1] => Array
(
[uid] => 1
[ticket_code] => 2oeii8hm
)
[2] => Array
(
[uid] => 1
[ticket_code] => m0dwtjiw
)
[3] => Array
(
[uid] => 1
[ticket_code] => q6c7cymb
)
[4] => Array
(
[uid] => 1
[ticket_code] => zyqhm5bj
)
[5] => Array
(
[uid] => 1
[ticket_code] => amdqzjpi
)
[6] => Array
(
[uid] => 2
[ticket_code] => tzql7l42
)
[7] => Array
(
[uid] => 2
[ticket_code] => gap0r6vf
)
[8] => Array
(
[uid] => 2
[ticket_code] => ypqum5yz
)
[9] => Array
(
[uid] => 4
[ticket_code] => smupluac
)
[10] => Array
(
[uid] => 4
[ticket_code] => 9d8jsha7
)
[11] => Array
(
[uid] => 5
[ticket_code] => 6hdnja42
)
)
And I need you to get 3 "ticket_code" but no right to repeat the "uid".
I've been on trying as follows, but also repeats the "uid".
$ticketsWinners = array();
for ($i=0; $i < 3; $i++) {
$aux = array_rand($allTickets);
$aux2 = $allTickets[$aux]['uid'];
$ticketsWinners[] = array(
'uid' => $aux2,
'ticket_code' => $allTickets[$aux]['ticket_code']
);
}
Any way to do it without repeats?
We thank you in advance if anyone knows of something ^^
Try something like:
$ticketsWinners = array();
while (sizeof($ticketsWinners) < 3) {
$aux = array_rand($allTickets);
// array_rand return array of keys so you need first value only
$uid = $allTickets[$aux[0]]['uid']
// add uid as a key so ass not tot check all $allTickets values
if (!isset($ticketsWinners[$uid]))
$ticketsWinners[$uid] = $allTickets[$aux[0]];
}
// if you need $allTickets back to numeric keys [0, 1, 2]
$allTickets = array_values($allTickets);
if you're afraid of infinite loops (that can take place really) then try this:
$ticketsWinners = array();
// shuffle array before checking
shuffle($allTickets);
foreach ($allTickets as $tick_data) {
$uid = $tick_data['uid'];
if (!isset($ticketsWinners[$uid]))
$ticketsWinners[$uid] = $tick_data;
if (sizeof($ticketsWinners) == 3)
break;
}
Here in worst case you check $allTickets array and get winners of size <= 3.
Try this:
$ticketsWinners = array();
$ticketUid = array();
for ($i=0; $i < 3; $i++) {
$aux = array_rand($allTickets);
$aux2 = $allTickets[$aux]['uid'];
if(! in_array($aux2, $ticketUid)) {
$ticketUid[$i] = $aux2;
$ticketsWinners[] = array(
'uid' => $aux2,
'ticket_code' => $allTickets[$aux]['ticket_code']
);
} else {
$i--;
}
}
this structure would be better ( added benefit of ticket numbers being unique )
$tickets = Array
(
'0oreb8yo' => 1,
'2oeii8hm' => 1,
'm0dwtjiw' => 1,
'q6c7cymb' => 1,
'zyqhm5bj' => 1,
'amdqzjpi' => 1,
'tzql7l42' => 2,
'gap0r6vf' => 2,
'ypqum5yz' => 2,
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
);
$winners = array();
$picks = 3;
for($i = 0; $i < $picks; $i++){
if(count($tickets) == 0 ){
break; //or error -- shouldn't need this unless picks exceed uids
}
$ticket = array_rand($tickets);
$winner = $tickets[$ticket];
$winners[] = $winner;
$tickets = array_filter($tickets, function($item) use ($winner){
return $winner != $item;
});
}
echo '<pre>';
var_export($winners);
outputs
array (
0 => 2,
1 => 1,
2 => 4,
)
array (
0 => 2,
1 => 1,
2 => 3,
)
array (
0 => 1,
1 => 3,
2 => 2,
)
unlike the while option, this will reduce the operations for each loop of the for loop by reducing the ticket array by the uid. It's also the only way to insure your not always pulling out a user with tickets, what if user 1 bought 90% of the tickets, you'd loop on him 90% of the time, in any case you have to reduce the ticket array by winners if they can win only once. In essence you remove each uid from the list when they win. You can also be sure that each ticket has the same chance to win ( as well as array_rand is random that is ) - they all have equal footing.
ticket array reduction
after loop1
array (
'tzql7l42' => 2,
'gap0r6vf' => 2,
'ypqum5yz' => 2,
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
)
after loop2
array (
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
)
after loop3
array (
'smupluac' => 3,
'6hdnja42' => 5,
)
winners
array (
0 => 1,
1 => 2,
2 => 4,
)
to return both the uid and wining ticket change
$winners[] = $winner;
to
$winners[$ticket] = $tickets[$ticket];
now winners will be, just like the input array
ticketnumber => uid
ticket is the key ( which is the ticket ) and winner is the value ( which is the uid )

Parsing array values into multidimensional array

I have some data I retrieve from a JSON feed that currently is being parsed into an array like this: (simplifying for demonstration purposes)
So pretty much an array returns a movie theater name, with the showtimes associated with that particular theater.
[0] => American Theater
[1] => 2014-06-04T13:10
[2] => 2014-06-04T15:10
[3] => Grand Theater
[4] => 2014-06-04T15:30
[5] => 2014-06-04T19:10
How would I parse this array to be multidimensional, for instance:
Array
(
[0] => Array
(
[theater] => Array
(
[name] => American Theater
)
[showtimes] => Array
(
[1] => 2014-06-04T13:10
[2] => 2014-06-04T15:10
)
)
[1] => Array
(
[theater] => Array
(
[name] => Grand Theater
)
[showtimes] => Array
(
[1] => 2014-06-04T15:30
[2] => 2014-06-04T19:10
)
)
)
I'm assuming you're trying to access some api and have no control over how the data is passed back to you? If you do then the API should be responsible for returning a sensible schema.
But if you're forced to work with this array and the amount of showtimes are unknown to you, then you can do something like this:
$array = array(
'American Theater',
'2014-06-04T13:10',
'2014-06-04T15:10',
'Grand Theater',
'2014-06-04T15:30',
'2014-06-04T19:10'
);
$i = 0;
foreach ($array as $key => $value) {
if (strtotime($value)) {
$theaters[$i - 1]['showtimes'][] = $value;
}
else {
$theaters[$i]['theater']['name'] = $value;
$i++;
}
}
Outcome
To walk you through it, $array is whatever the returned dataset is. We set an index in the $i value and want to only increment it if we determine we've detected a theater name. Within the loop we first try to determine if the string can be converted to a php time value. If it cannot we add the theater name to our new schema structure, and increment our index value. Since times are always added to theater names, we are expecting the first index number to always be one higher than what we want to add the showtime to.
This will fail to be accurate in cases when a theater name is convertible to a time value in such cases like Next Month. There are a couple of other ways to solve this with regex or by inspecting the string for certain characters and their position since the time format will remain the same.
You could replace the strtotime() with:
$str = str_split($value);
if (($str[4] && $str[7]) == '-' && $str[10] == 'T' && $str[13] == ':' ) {
$theaters[$i - 1]['showtimes'][] = $value;
}
If you want such structure, you need to create a new copy of it. You may also need to chunk/group them by three's using array_chunk first, and then, from there, you can loop it now and start creating the desired format.
Consider this example:
$old_values = array('American Theater', '2014-06-04T13:10', '2014-06-04T15:10', 'Grand Theater', '2014-06-04T15:30', '2014-06-04T19:10');
$old_values = array_chunk($old_values, 3);
$new_values = array();
foreach($old_values as $key => $value) {
$new_values[] = array(
'theater' => array('name' => $value[0]),
'showtimes' => array(1 => $value[1], 2 => $value[2]),
);
}
Edit: As mentioned, one theater can have many showtimes, therefore this current solution will fail. This may be an alternative (you may need to check each element if its a theater name or a date). Consider this example:
$old_values = array(
'American Theater',
'2014-06-04T13:10',
'2014-06-04T15:10',
'Grand Theater',
'2014-06-04T15:30',
'2014-06-04T19:10',
'Magic Johnson Theater',
'2014-06-04T19:10',
'2014-06-04T19:10',
'2014-06-04T19:10',
'Mall of America Theater',
'2014-06-04T19:10',
'2014-06-04T19:10',
'2014-06-04T19:10',
'2014-06-04T19:10',
);
$new_values = array();
$current_key = 0;
foreach($old_values as $key => $value) {
$current_value = $value;
$pieces = explode('T', $current_value);
$dates = explode('-', $pieces[0]);
if(count($dates) == 3) {
$new_values[$current_key]['showtimes'][] = $current_value;
} else {
$current_key++;
$new_values[$current_key]['theater']['name'] = $current_value;
}
}
Sample Output:
Array
(
[1] => Array
(
[theater] => Array
(
[name] => American Theater
)
[showtimes] => Array
(
[0] => 2014-06-04T13:10
[1] => 2014-06-04T15:10
)
)
[2] => Array
(
[theater] => Array
(
[name] => Grand Theater
)
[showtimes] => Array
(
[0] => 2014-06-04T15:30
[1] => 2014-06-04T19:10
)
)
[3] => Array
(
[theater] => Array
(
[name] => Magic Johnson Theater
)
[showtimes] => Array
(
[0] => 2014-06-04T19:10
[1] => 2014-06-04T19:10
[2] => 2014-06-04T19:10
)
)
[4] => Array
(
[theater] => Array
(
[name] => Mall of America Theater
)
[showtimes] => Array
(
[0] => 2014-06-04T19:10
[1] => 2014-06-04T19:10
[2] => 2014-06-04T19:10
[3] => 2014-06-04T19:10
)
)
)
Sample Fiddle

how to get number out of an array key

I have the following array:
Array
(
[Ingredient 1 Amount] => Array
(
[0] => 2
)
[Ingredient 1] => Array
(
[0] => lemon juice (t fresh-squeezed)
)
[Ingredient 2 Amount] => Array
(
[0] => 3
)
[Ingredient 2] => Array
(
[0] => 1/2 cups peeled and diced potatoes
)
[Ingredient 3 Amount] => Array
(
[0] => 1/3
)
[Ingredient 3 Size] => Array
(
[0] => cup
)
[Ingredient 3] => Array
(
[0] => diced celery
)
[Ingredient 4 Amount] => Array
(
[0] => 1/3
)
[Ingredient 4 Size] => Array
(
[0] => cup
)
[Ingredient 4] => Array
(
[0] => finely chopped onion
)
[Ingredient 5 Amount] => Array
(
[0] => 5
)
[Ingredient 5 Size] => Array
(
[0] => tablespoons
)
[Ingredient 5] => Array
(
[0] => pickles
)
)
Each ingredient set consists of Amount, Size and just the Default. There may be one of each or there may be some missing, so I can't just count the array and divide by 3. I need to know how many ingredient sets are in the array. This is shown by the last ingredient set, so in this case I would want the number "5". How would I go about getting that?
--EDIT--
The array is being built from custom post fields in wordpress. It's what I get back from
$post_meta = get_post_meta($id);
In my opinion you should re-structure you're array. It is not optimised.
Try something along the lines of (this is psuedo code to give the idea)
[Ingredient 1] => Array
(
[type] => lemon juice (t fresh-squeezed),
[Amount] => lemon juice (t fresh-squeezed),
[Size] => lemon juice (t fresh-squeezed)
)
As I've mentioned in my comments restructuring your array would be far better, as your current structure isn't helpful for what you want to do. However, assuming that isn't an option this is what to do:
$key = end(array_keys($array));
$num = substr($key, 11);
You can see an example of this in work here on eval.in.
This will work if the last key won't always be the one you want:
$keyArr = array_keys($array);
$keyRev = array_reverse($keyArr);
foreach($keyRev as $key) {
if(stripos($key, 'amount') === FALSE && stripos($key, 'size') === FALSE) {
$num = substr($key, 11);
break;
}
}
echo $num;
You can see an example of this in work here on eval.in.
you can find maximum like this
$max = 0;
foreach($array as $key => $value) {
if(intval(str_replace("Ingredient ", "", $key)) > $max) {
$max = intval(str_replace("Ingredient ", "", $key));
}
}
NOTICE: as Styphon mentioned in comments if you want to get last element number, it's not necessary to loop through whole array, only process last element
$a = array(
array(['ingediant1'] =>
array(
'amount' => 1,
'description' => 'whatever'
)
)
);
$amount = 0;
foreach ($a as $ingediant => $info) {
$amount += $info['amount'];
}
If you'd restructure your array to something like I did, you should be able to get the total amount of ingredients easy.

Sorting sub array values in MultiDimension Array

I am trying to sort an array sent from an XML feed.
The Array looks like this from print_r($answer);:
Array
(
[size] => Array
(
[0] => 1.5m x 1.5m
[1] => 1.5m x 3m
[2] => 3m x 6.0m
[3] => 3m x 2.3m
)
[rate] => Array
(
[0] => 80
[1] => 135
[2] => 295
[3] => 180
)
[sortorder] => Array
(
[0] => 3
[1] => 4
[2] => 1
[3] => 2
)
.
.
.
)
I want to get out the array:
Array
(
[size] => Array
(
[0] => 3m x 6.0
[1] => 3m x 2.3m
[2] => 1.5m x 1.5m
[3] => 1.5m x 3m
)
[rate] => Array
(
[0] => 295
[1] => 180
[2] => 80
[3] => 135
)
[sortorder] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
.
.
.
)
What I am trying to do is use the Sort Order sub array to display the items in that order
I have tried a number of uasort() and array_multisort() examples, but all seem to order the sub arrays and not the values inside the sub arrays
Any ideas will be a great help. Cheers
This one sorts $answer['sortorder'] and uses those keys to sort the rest of $answer without restructuring first.
// sort $answer['sortorder'] and retrieve indices.
asort($answer['sortorder']);
$idx = array_keys($answer['sortorder']);
// do sorting
$sorted = array();
foreach($answer as $key=>$subarr) {
if ($key != 'sortorder') { // don't sort
foreach($idx as $i) {
$sorted[$key][] = $subarr[$i];
}
} else {
// $answer['sortorder'] is already sorted.
$sorted[$key] = $subarr;
}
}
print_r($sorted);
See it in action here.
This approach will re-structure the array with the sort order being the index, sort the array, then return it to it's original structure.
echo '<pre>';
$array['size'][0] = '1.5m x 1.5m';
$array['size'][1] = '1.5m x 3m';
$array['size'][2] = '3m x 6.0m';
$array['size'][3] = '3m x 2.3m';
$array['rate'][0] = 80;
$array['rate'][1] = 135;
$array['rate'][2] = 295;
$array['rate'][3] = 180;
$array['sortorder'][0] = 3;
$array['sortorder'][1] = 4;
$array['sortorder'][2] = 1;
$array['sortorder'][3] = 2;
$temp = array();
foreach($array['sortorder'] as $key => $value)
{
$temp[$array['sortorder'][$key]] = array(
'size'=>$array['size'][$key],
'rate'=>$array['rate'][$key],
'sortorder'=>$array['sortorder'][$key]
);
}
ksort($temp);
$array = array();
foreach($temp as $key => $value)
{
$array['size'][] = $value['size'];
$array['rate'][] = $value['rate'];
$array['sortorder'][] = $value['sortorder'];
}
print_r($array);
May I propose to use a different array structure that bundles each size, rate and sort order into one item:
array(
array('size' => '...', 'rate' => '...', 'sort order' => '...'),
...
)
That makes it trivial to sort, and in fact easier to work with in general.
This PHP 5.3+ code does this transformation and sorting:
$answer = array_map(function ($size, $rate, $sortorder) {
return compact('size', 'rate', 'sortorder');
}, $answer);
usort($answer, function ($a, $b) { return $a['sortorder'] - $b['sortorder']; });

PHP: A clean way of converting an array

What is a clean way to convert an array that looks like this:
[584] => Array ( [link_id] => 1 [site_id] => 5 [COUNT(*)] => 2 )
[585] => Array ( [link_id] => 243 [site_id] => 5 [COUNT(*)] => 2 )
[586] => Array ( [link_id] => 522 [site_id] => 89223 [COUNT(*)] => 3 )
To an array where the key is the site_id from above, so for the above example the resulting array would be:
[5] => Array( 1, 2, 243, 2) //Even ones and 0 are link_id, odd ones are count(*)
[89223] => Array(522, 3)
So, basically, group them by site_id. I still need to keep the relationship between link_id and count(*), in the case above I am doing it by the positions (so 0 and 1 are together, 2 and 3, etc) but I am open to a new structure as well.
Thanks!
You mean something like this? (Demo):
$out = array();
foreach($input as $v)
{
$site_id = $v['site_id'];
unset($v['site_id']);
$out[$site_id][] = $v;
}
Or in case you prefer value pairs after each other (Demo):
...
unset($v['site_id']);
$out[$site_id][] = array_shift($v);
$out[$site_id][] = array_shift($v);
} ...

Categories