PHP - Split multidimensional array unique values - php

Think if I start with an explanation first it will help... I have a competition in which there are 3 winners in each County (The ones with the highest votes).
My current array looks like this:
Array
(
[0] => Array
(
[entryID] => 1
[votes] => 3
[countyID] => 46
)
[1] => Array
(
[entryID] => 4
[votes] => 1
[countyID] => 2
)
[2] => Array
(
[entryID] => 2
[votes] => 0
[countyID] => 46
)
[3] => Array
(
[entryID] => 5
[votes] => 0
[countyID] => 46
)
)
What I need to do here is figure out a way of finding the top 3 highest votes within each of the CountyID's.
Any ideas how I can achieve this?
Thanks,
Scott.

The simplest way to do it is to re-organize your array so the country id is the top level index, and then just write a simple custom function to sort the vote count in descending order... that way the top voted entries are on top.
$entries = array(
array('entryId' => 1, 'votes' => 3, 'countryId' => 46),
array('entryId' => 4, 'votes' => 1, 'countryId' => 2),
array('entryId' => 2, 'votes' => 0, 'countryId' => 46),
array('entryId' => 5, 'votes' => 0, 'countryId' => 46),
);
// Sort votes in descending order (most on top)
function voteSort($a, $b) {
if ($a['votes'] == $b['votes']) {
return 0;
}
return ($a['votes'] < $b['votes']) ? 1 : -1;
}
// Re-organize the array with country as top level
$byCountry = array();
foreach ($entries as $entry) {
$byCountry[$entry['countryId']][] = array(
'entryId' => $entry['entryId'],
'votes' => $entry['votes']
);
}
// For each country, sort by votes
foreach ($byCountry as $index => $country) {
usort($byCountry[$index], 'voteSort');
}
That should work.

You can split the array by unique country IDs in this way:
$entries = array(
array('entryID' => 1, 'votes' => 3, 'countryID' => 46),
array('entryID' => 4, 'votes' => 1, 'countryID' => 2),
array('entryID' => 2, 'votes' => 0, 'countryID' => 46),
array('entryID' => 5, 'votes' => 0, 'countryID' => 46),
);
$entriesByCountry = array();
foreach($entries as $entry) {
if(!array_key_exists($entry->countyID, $entriesByCountry)
$entriesByCountry[$entry->countyID] = array();
$entriesByCountry[$entry->countyID][] = $entry;
}
Than sort each country-array by votes and take the first 3.

The function ksort() will be helpful.
You can sort your array firstly with the rank and second the country. So you will have all of the firsts in every country on the begining of your array, then the seconds, etc.
The function you will pass to ksort will be kind of
function compareVote($a,$b){
if($a['votes'] > $b['votes'])
return 1;
if($a['votes'] == $b['votes']){
if($a['countyID'] > $b['countyID'])
return 1;
if($a['countyID'] == $b['countyID'])
return 0;
return -1;
}
return -1;
}
View the official doc page.

Related

How to subtract all column values in multi-dimensional array in Php?

How can I subtract all the columns values? My array is like
Array
(
[0] => Array
(
[id] => 1
[web_traffic] => 442
[form_users] => 131
[date] => 20181004
)
[1] => Array
(
[id] => 2
[web_traffic] => 102
[form_users] => 15
[date] => 20181003
)
[2] => Array
(
[id] => 3
[web_traffic] => 387
[form_users] => 97
[date] => 20181002
)
)
I need to subtract the each column(except date & id) and get the result based on date(Ascending order). For example 20181004 means 4th October 2018. My output should like the below
Array
(
[web_traffic] => -152
[form_users] => -49
)
My code took reference from How to sum all column values in multi-dimensional array?
foreach ($data as $value) {
unset($value[ 'id' ]);
$time = date('Ymd', strtotime($value[ 'date' ]));
if (in_array($time, $dates)) {
$value[ 'date' ] = $time;
foreach ($value as $key => $secondValue) {
if ( !isset($output[ $key ])) {
$output[ $key ] = 0;
}
$output[ $key ] -= $secondValue;
}
}
}
Use PHP array_reduce() and array_column() like this:
$initial_array = array(array('id' => 1,
'web_traffic' => 442,
'form_users' => 131,
'date' => 20181004),
array('id' => 2,
'web_traffic' => 102,
'form_users' => 15,
'date' => 20181003),
array('id' => 3,
'web_traffic' => 387,
'form_users' => 97,
'date' => 20181002));
function sum($carry, $item)
{
$carry -= $item;
return $carry;
}
$web_traffic = array_column($initial_array, "web_traffic");
$form_users = array_column($initial_array, "form_users");
$date = array_column($initial_array, "date");
array_multisort($date, SORT_ASC, $form_users, SORT_DESC, $initial_array);
$result_array = Array(
"web_traffic" => array_reduce(array_column($initial_array, "web_traffic"), "sum",2*$initial_array[0]['web_traffic']),
"form_users" => array_reduce(array_column($initial_array, "form_users"), "sum",2*$initial_array[0]['form_users'])
);
print_r($result_array);
I would first sort the array using usort() in example.
Sorting first, because that can be tricky to substract in 1 loop the oldest datas to the newers one.
Separating intentions provide a cleaner code, easier to maintain.
The dates don't need to be converted into a date. The string is well formatted and can be used as is in a comparison and a sort. "20170101" < "20180101", "20180101" < "20181001" and "20181002" < "20181004"
When done, I'll save the first values as initial values to be used in substract.
Then, loop the sorted array and substract the 'web_traffic' and 'form_users' to the initial values.
$datas = array(array('id' => 1,
'web_traffic' => 442,
'form_users' => 131,
'date' => 20181004),
array('id' => 2,
'web_traffic' => 102,
'form_users' => 15,
'date' => 20181003),
array('id' => 3,
'web_traffic' => 387,
'form_users' => 97,
'date' => 20181002));
//If needed, you can backup the original array, because usort() will modify it.
$backUpDatas = $datas;
//Sort
usort($datas, function ($arr1, $arr2)
{
if (isset($arr1['date']) && isset($arr2['date']))
{
if ($arr1['date'] == $arr2['date'])
return (0);
return (($arr1['id'] > $arr2['id']) ? (-1) : (1));
}
});
//Initial values
$finalValues['web_traffic'] = $datas[0]['web_traffic'];
$finalValues['form_users'] = $datas[0]['form_users'];
//Substract
foreach ($datas as $key => $value)
{
if ($key > 0)
{
if (isset($value['web_traffic']))
$finalValues['web_traffic'] -= $value['web_traffic'];
if (isset($value['form_users']))
$finalValues['form_users'] -= $value['form_users'];
}
}
var_dump($finalValues);
Output :
array (size=2)
'web_traffic' => int -157
'form_users' => int -49

Best way to calculate overall records based on associative data

I'm processing the final results of competitions and its general report on the best trainer and which place the trainer should get.
I have already prepared associative arrays below. The key represents trainer's id and the value represents the number of medals in a category (gold, silver, bronze) that his/her athletes got.
[gold] => Array
(
[777777] => 4
[333333] => 2
[555555] => 1
[999999] => 1
)
[silver] => Array
(
[999999] => 3
[777777] => 3
[333333] => 2
)
[bronze] => Array
(
[333333] => 6
[777777] => 4
[999999] => 2
)
Next array associates trainer's id with its name:
[trainers] => Array
(
[333333] => Trainer 4
[777777] => Trainer 1
[999999] => Trainer 2
[555555] => Trainer 3
)
I have stuck processing the data above into final results like this. Any ideas on how it could be done elegantly? The problem is that the data is never constant and the size of the array is always different.
Any help would be greatly appreciated.
Here is code sample:
$gold, $silver, $bronze, $trainers are arrays with information you provided.
$out = [];
foreach($trainers as $trainerId=> $trainerName){
$out[] = array(
'id'=>$trainerId,
'name'=>$trainerName,
'gold'=>isset($gold[$trainerId])?$gold[$trainerId]:0,
'silver'=>isset($silver[$trainerId])?$silver[$trainerId]:0,
'bronze'=>isset($bronze[$trainerId])?$bronze[$trainerId]:0,
);
}
uasort($out, function($a, $b){
// Here: sort by your algorithm. Here is example:
if($a['gold'] != $b['gold']){
return $b['gold'] - $a['gold'];
}
if($a['silver'] != $b['silver']){
return $b['silver'] - $a['silver'];
}
return $b['bronze'] - $a['bronze'];
});
$placeId = 1;
foreach($out as &$info){
$info['place'] = $placeId++;
}
unset($info);
foreach($out as $info){
echo "{$info['place']} place goes to - {$info['name']} ({$info['id']}) as he/she got {$info['gold']} gold medals, {$info['silver']} silver and {$info['bronze']} bronze";
}
Here is another way to do it with metrics:
$gold = array
(
'777777' => 4,
'333333' => 2,
'555555' => 1,
'999999' => 1
);
$silver = array
(
'999999' => 3,
'777777' => 3,
'333333' => 2
);
$bronze = array
(
'333333' => 6,
'777777' => 4,
'999999' => 2
);
$trainers = array
(
'333333' => 'Trainer 4',
'777777' => 'Trainer 1',
'999999' => 'Trainer 2',
'555555' => 'Trainer 3'
);
$metrics = [
'gold' => 3,
'silver'=> 2,
'bronze' => 1];
$results = [];
foreach ($metrics as $arrName => $metric)
{
foreach (${$arrName} as $trainerId => $medals)
{
$results[$trainerId] = ( isset($results[$trainerId]) ) ? $results[$trainerId]+$medals * $metric : $medals * $metric;
}
}
// sorting scores (by value)
arsort($results);
// print scores
var_dump($results);
// print final results
$placeOut = '';
foreach ($results as $trainerId => $score) {
$placeOut .= $trainers[$trainerId].": he/she has ";
foreach ($metrics as $medalName => $metric) {
$placeOut .= (${$medalName}[$trainerId] > 0 ? ${$medalName}[$trainerId] : 0)." ".$medalName.", ";
}
$placeOut .= "\n";
}
echo "<pre>".$placeOut."</pre>";
?>

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 )

need soting multi dimensional array based on another array displaying order

I need sorting array based another array sort value.
Actual array : array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65))
sorting order array : array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14)
expected result array:
array(
name=>'JK',
year=>array(
marks1=>array(
sub1=>50,
sub3=>70,
sub4=>35,
sub5=>75
sub7=>65
),
marks2=>array(
sub8=>50,
sub9=>35,
sub10=>70,
sub11=>65,
sub12=>75
),
age=>'20',
place=>'India'
)
I hope this will help :)
$array1 = array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65));
$array2 = array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14);
//final array
$final_array = array();
//for each value in sorting array
foreach ($array2 as $key => $value)
{
//store result in final array
$final_array[$value] = $array1[$key];
}
//display array for check result
var_dump($final_array);
I am not exactly sure what is being asked. However, I will take a shot. I think the function you are looking for is uksort.
<?php
$array1 = array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65)));
function sorter($a,$b)
{
$array2 = array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14);
return $array2[$a] > $array2[$b];
}
uksort($array1, "sorter");
var_dump($array1);
?>
Here is an example of it running on codepad. You will probably have to work out a bit more since the subs are not sorted. But, possibly is_array can help you out.
$arr1 = array(
'name' => 'JK',
'age' => 20,
'place' => 'India',
'year' =>
array(
'marks1' =>
array('sub1' => 50,
'sub3' => 70,
'sub7' => 65,
'sub5' => 75,
'sub4' => 35),
'marks2' =>
array('sub8' => 50,
'sub10' => 70,
'sub12' => 75,
'sub9' => 35,
'sub11' => 65)));
$arr2 = array('name' => 1, 'year' => 2, 'age' => 3, 'place' => 4, 'sub1' => 5, 'sub3' => 6, 'sub4' => 7, 'sub5' => 8, 'sub7' => 9, 'sub8' => 10, 'sub9' => 11, 'sub10' => 12, 'sub11' => 13, 'sub12' => 14);
foreach ($arr1['year'] as $key => &$value){
uksort($value, function ($a, $b) use($arr2){
return $arr2[$a] - $arr2[$b];
});
}
uksort($arr1, function ($a, $b) use($arr2){
return $arr2[$a] - $arr2[$b];
});
print_r($arr1);
Output:
Array
(
[name] => JK
[year] => Array
(
[marks1] => Array
(
[sub1] => 50
[sub3] => 70
[sub4] => 35
[sub5] => 75
[sub7] => 65
)
[marks2] => Array
(
[sub8] => 50
[sub9] => 35
[sub10] => 70
[sub11] => 65
[sub12] => 75
)
)
[age] => 20
[place] => India
)

PHP - Reorder array to match the order of another array

I have 1 array that has the right values that I need but it is out of order. I then have another array with the same keys and it is in the right order but the values are not what I need.
Here is my first array with the correct values but is out of order:
Array
(
[countTotal] => 7268
[zip] =>
[yearName] =>
[countZipRadius] =>
[Acura] => 1334
[Cadillac] => 511
[Ford] => 5423
)
Here is my second array with the right order but the wrong values:
Array
(
[countZipRadius] => 0
[zip] => 1
[yearName] => 2
[Acura] => 3
[Cadillac] => 4
[Ford] => 5
[countTotal] => 6
)
I am trying to figure out a way to create a new array with the right values from array 1 but that is in the order of array 2.
I have been playing with it for awhile and cannot seem to get it.
Any help would be great.
Thanks!
$c = array();
foreach (array_keys($b) as $k) {
$c[k] = $a[k];
}
You could use php's array_multisort function:
$original = array(
'countTotal' => 7268,
'zip' => '',
'yearName' => '',
'countZipRadius' => '',
'Acura' => 1334,
'Cadillac' => 511,
'Ford' => 5423,
);
$right = array(
'countZipRadius' => 0,
'zip' => 1,
'yearName' => 2,
'Acura' => 3,
'Cadilac' => 4,
'Ford' => 5,
'countTotal' => 6
);
//make sure both arrays are in the same order
ksort($original);
ksort($right);
array_multisort($right, $original);
print_r($original);
When you give it two arrays with the same number of elements it sorts both arrays, based on the order of the first array - in this case the 0, 1, 2, 3, etc. values in $right
Create a New Array (Array C)
Use a FOR loop to go through Array B
For each value in Array B, get the value with the same key from Array A and set Array C append those values to Array C. This will put them in the correct order in C.
Using scones' method:
$original = array(
'countTotal' => 7268,
'zip' => '',
'yearName' => '',
'countZipRadius' => '',
'Acura' => 1334,
'Cadillac' => 511,
'Ford' => 5423,
);
$right = array(
'countZipRadius' => 0,
'zip' => 1,
'yearName' => 2,
'Acura' => 3,
'Cadilac' => 4,
'Ford' => 5,
'countTotal' => 6
);
foreach ($right as $key => $value) {
$new[$key] = $original[$key];
}
print_r($new);
$array = array('a' => 100, 'b' => '5');
$newArray = array_combine(array_keys($array), range(0, count($array) - 1));
var_dump($newArray);

Categories