Code: I have a condition on this, but it's not clear for me
$check_account1 = false;
$check_account2 = false;
$user1_amount = 100;
$user2_amount = 100;
if ($account->type == "A") {
$check_account1 = true;
$amount = 100;
if ($check_account1) {
$amount = $user1_amount;
} else {
$amount += $user1_amount;
}
} elseif ($account->type == "B") {
$amount = $user2_amount;
}
$insert = new Amount;
$insert->user_id = $account->id;
$insert->amount = $amount;
$insert->save();
EDIT: I have a users which has a rank of Basic, Junior, Premium, Advanced, Senior, which is every user has it's corresponding amount based on their rank.
If someone is not present or not available, the amount based on his rank will go to the rank next to him, if the user is present he will receive the amount based on his rank. So if Basic, Junior, Premium and Advanced is not present, the sum of their corresponding amount will go to Senior
Present meaning it's in the database table, if the loop doesn't get a user with that rank, I call it absent.
This is how it goes:
Basic - 10
Junior - 20
Premium - 30
Advanced - 40
Senior - 50
$amount_to_transfer = 0;
$amount_to_receive = 0;
foreach($users as $user){
if($user->rank == 'Basic'){
// basic is present, add 10 to himself, if not 10 will go to the next rank which is Junior
$amount->amount = 10;
$amount->save();
}
else{
//
}
}
Because you want to loop through your users, and transfer amount from those that are not present to the first next user that is present, here is the code that should accomplish that task:
$amount_to_transfer = 0;
$amount_to_receive = 100;
foreach($users as $user){
if($user->present){
$user->amount += $amount_to_receive + $amount_to_transfer;
$amount_to_transfer = 0;
// If you want to save the user here
// $user->save();
}
else{
$amount_to_transfer += $amount_to_receive;
}
}
I assumed that your user model has present property which tells if the user is present or not (boolean value) and that the amount is stored in amount field. You can adapt this condition based on your properties in your User model.
All in all, you just need to define a transfer amount, which starts off as zero and add it alongside with receiving amount every time the user is present. If user is not present, just increment the amount to transfer so it can be added to first next present user.
Adapted
Here is the adapted code, I used dummy collections and ranks. Please adapt this to your settings. In this example there is no Advanced rank, so senior will get 50 + 40 amount.
$ranks = collect([
(object) ['name' => 'Basic', 'amount' => 10],
(object) ['name' => 'Junior', 'amount' => 20],
(object) ['name' => 'Premium', 'amount' => 30],
(object) ['name' => 'Advanced', 'amount' => 40],
(object) ['name' => 'Senior', 'amount' => 50],
]);
$users = collect([
(object) ['rank' => 'Basic', 'amount' => 0],
(object) ['rank' => 'Junior', 'amount' => 0],
(object) ['rank' => 'Premium', 'amount' => 0],
(object) ['rank' => 'Senior', 'amount' => 0],
(object) ['rank' => 'Basic', 'amount' => 0],
]);
$amount_to_transfer = 0;
$available_ranks = $users->pluck('rank');
foreach($ranks as $rank){
if(!in_array($rank->name, $available_ranks->toArray()))
{
$amount_to_transfer += $rank->amount;
}
else{
$usersOfThisRank = $users->filter(function($usr, $key) use($rank){
return $usr->rank == $rank->name;
});
$usersOfThisRank->map(function($usr, $key) use($rank, $amount_to_transfer){
$usr->amount += $rank->amount + $amount_to_transfer;
// $usr->save();
});
$amount_to_transfer = 0;
}
}
Related
I'm trying to output [device_id] of the least frequent [device_ip_isp] from this array.
Also, If the array only has two SharedDevice Object available, and having different [device_ip_isp], it should output the 2nd SharedDevice Object's [device_id]
array (
0 =>
SharedDevice::__set_state(array(
'device_no' => 1,
'device_id' => '82',
'device_ip_isp' => 'Verizon Fios',
)),
1 =>
SharedDevice::__set_state(array(
'device_no' => 2,
'device_id' => '201',
'device_ip_isp' => 'Spectrum',
)),
2 =>
SharedDevice::__set_state(array(
'device_no' => 3,
'device_id' => '312',
'device_ip_isp' => 'Verizon Fios',
)),
3 =>
SharedDevice::__set_state(array(
'device_no' => 4,
'device_id' => '9715',
'device_ip_isp' => 'Verizon Fios',
)),
4 =>
SharedDevice::__set_state(array(
'device_no' => 5,
'device_id' => '11190',
'device_ip_isp' => 'Verizon Fios',
)),
)
The output should be 201 because "Spectrum" is the least frequent.
I tried the following and had issues:
I'm not sure how I can sort the object variables before comparing to find the least frequent.
/*
$user->getUser_devices() will output the array shown above.
*/
leastFrequent($user->getUser_devices(), 5);
function leastFrequent($arr, $n){
// find the min frequency
// using linear traversal
$min_count = $n + 1;
$res = -1;
$curr_count = 1;
for ($i = 1; $i < $n; $i++) {
if ($arr[$i]['device_ip_isp'] == $arr[$i - 1]['device_ip_isp']) {
$curr_count++;
} else {
if ($curr_count < $min_count) {
$min_count = $curr_count;
$res = $arr[$i - 1]['device_id'];
}
$curr_count = 1;
}
}
// If last element is
// least frequent
if ($curr_count < $min_count) {
$min_count = $curr_count;
$res = $arr[$n - 1]['device_id'];
}
return $arr[$n]$res['device_id'];
}
Ok, below is the explanation of the snippet inside out.
array_column to get all the device_ip_isp values in a single array.
array_count_values to get the frequency of each value.
array_reverse to reverse the count frequency array since you need the latest share device ID incase of count collision for a minimum count value.
min to get the lowest value among all the frequency counts.
array_search to get the key of the first frequency min element.
In the end, we reverse the input array to immediate return the device IP the moment we find the key from the above step matching the current device_ip_isp.
Snippet:
<?php
function getLeastFrequentElement($arr){
$freqs = array_reverse(array_count_values(array_column($arr, 'device_ip_isp')));
$deviceIPISP = array_search(min($freqs), $freqs);
foreach(array_reverse($arr) as $sharedDevice){
if($sharedDevice->device_ip_isp === $deviceIPISP){
return $sharedDevice->device_id;
}
}
throw new Exception("No shared object found!");
}
echo getLeastFrequentElement($arr);
Online Demo
I am trying to generate multiple invoices in a school management system.
Here is the issue:
For example I have 10 students in the DB
I am able to generate the invoices if the students are selected in consecutive order.
Student 1, Student 2, Student 3, Student 4
But once I miss a student from the consecutive order and try to generate in a random order it gives me an undefined offset error:
Student 1, Student 4, Student 5, Student 7
Here is the code:
for($x = 1; $x <= count($this->input->post('studentId')); $x++) {
$insert_data = array(
'class_id' => $this->input->post('className'),
'section_id' => $this->input->post('sectionName'),
'student_id' => $this->input->post('studentId')[$x],
'payment_name_id' => $payment_name_id
);
$status = $this->db->insert('payment', $insert_data);
}
return ($status === true ? true : false);
The same also applies to editing the invoices once it has been generated.
I even tried changing the for loop to a while loop for that one, which does not give me any error but it just doesn't save if the students selected are in a random order:
$x = 1;
$form_fields = count($this->input->post('editStudentId'));
while($x <= $form_fields) {
if(!empty($this->input->post('editStudentId')[$x])) {
$update_payment_data = array(
'class_id' => $this->input->post('editClassName'),
'section_id' => $this->input->post('editSectionName'),
'student_id' => $this->input->post('editStudentId')[$x],
'payment_name_id' => $id
);
$status = $this->db->insert('payment', $update_payment_data);
}
$x++;
}
return ($status === true ? true : false);
IMAGE - SELECTING STUDENTS:
IMAGE- POST SAVE WITH JUST 2 out of the 4 I SELECTED:
Replace for with foreach:
foreach ($this->input->post('studentId') as $studentId) {
$insert_data = array(
'class_id' => $this->input->post('className'),
'section_id' => $this->input->post('sectionName'),
'student_id' => $studentId,
'payment_name_id' => $payment_name_id
);
$status = $this->db->insert('payment', $insert_data);
}
This is the input:
$deals = array(
array('deal' => '1', 'deal_date' => '2017-02-13', 'amount' => '400'),
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
array('deal' => '5', 'deal_date' => '2017-07-05', 'amount' => '10500'),
);
I am searching for a subset of exactly N elements where the sum of the 'amount' properties is greater then or equal to X and the elements have the lowest 'deal_date' property possible.
If there is no subset that fits the rules:
$subset = false;
So for N=2 and X=10000, I get this output:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
for N=3 and X=12000:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
My current idea entails creating an array that contains arrays of the list of deals in every conceivable order. Then I scan through those for my list of deals that fit the criteria but then I have a list of deals and I am unsure how to determine the 'earliest'.
I'm also looking for the algorithm with the lowest time complexity.
Any ideas would be appreciated.
It is not simple but roughly this
There will be a better way.
The approximate source is
$calculate = 0;
$end_case = array();
$sum = 10000;
// first amount 10000 over value array remove
foreach($deals as $key => $val){
if($val['amount']>=$sum)unset($deals[$key]);
}
// second Obtaining data
foreach($deals as $key => $val){
foreach($deals as $k => $v){
// same deal excetpion
if($val['deal']==$v['deal']) continue;
$calc = deal_sum($val['amount'],$v['amount']);
if($calc>=$sum){
echo "#".$v['deal']." => ".$val['amount']."+".$v['amount']." = ".$calc."\n";
array_push($end_case,$v['deal']);
}
print_r($end_case);
}
}
function deal_sum($source,$destination){
$result = $source+$destination;
return $result;
}
You should keep in mind the time complexity of your algorithm, otherwise large input sets will take forever.
Algorithm for N:
Returns array() of the first $number_of_deals_to_combine deals from
$deals ordered by 'deal_date', that have at least $threshold or
greater combined amounts or false if no such combination is found.
Uses a rolling window of $number_of_deals_to_combine elements
excluding deals that have too small 'amount'.
function combine_deals($deals, $threshold, $number_of_deals_to_combine){
$return = false;
//are there enough deals to combine?
if(count($deals) >= $number_of_deals_to_combine){
//are deals sorted by date? sort them
usort($deals,function($a, $b){return strnatcmp($a['deal_date'],$b['deal_date']);});
//find out if there is a possible solution, by adding up the biggest deals
$amounts = array_map('intval',array_column($deals,'amount'));
rsort($amounts); //sort descending
if(array_sum(array_slice($amounts, 0, $number_of_deals_to_combine)) >= $threshold){
//find the smallest possible number that is part of a solution
$min_limit = $threshold - array_sum(array_slice($amounts, 0, $number_of_deals_to_combine - 1));
//rolling window
$combined = array();
foreach($deals as $deal){
if(intval($deal['amount']) < $min_limit){continue;} //this deal is not part of any solution
$combined[] = $deal;
//keep the number of deals constant
if(count($combined) > $number_of_deals_to_combine){
$combined = array_slice($combined, count($combined) - $number_of_deals_to_combine);
}
//there are enough deals to test
if(count($combined) == $number_of_deals_to_combine){
$amount = array_sum(array_map('intval',array_column($combined, 'amount')));
if($amount >= $threshold){
$return = $combined;
break;
}
}
}
}
}
return $return;
}
$threshold = 10000;
$number_of_deals_to_combine = 2;
$result = combine_deals($deals, $threshold, $number_of_deals_to_combine);
Are the amount fields always integers? If not, replace all intval with floatval everywhere.
I'm writing a PHP script for available rooms in a hotel. I want every combination for a group (i.e. 4 person). This is my array.
$room_array = array(
array(
"title" => "1 person room",
"room_for" => 1,
"price" => 79
),
array(
"title" => "2 person room with other",
"room_for" => 1,
"price" => 69
),
array(
"title" => "2 person room alone",
"room_for" => 1,
"price" => 89
),
array(
"title" => "2 person",
"room_for" => 2,
"price" => 69
),
array(
"title" => "3 person",
"room_for" => 3,
"price" => 69
)
);
Possible outcome:
4x 1 person room
4x 2 person room with other
3x 1 person room + 1x 2 person room with other
2x 2 person room
1x 3 person room + 1x 1 person room
etc. etc.
This calls for a recursive function. But every example I looked at doesn't work with counting in the inner array. The closest i found was this question:
Finding potential combinations of numbers for a sum (given a number set to select from)
But i didn't get de solution to work..
UPDATE:
Hi, thanks for all the answers. Really helped me in finding the best practice. In the meantime, the assignment has changed a little so I can't answer my own original question. My problem is solved. Thanks again for the help!
My answer below will get you partway there.
Resources
I borrowed some code logic from this answer.
To quote the answer (in case of future removal), please view below.
You can try
echo "<pre>";
$sum = 12 ; //SUM
$array = array(6,1,3,11,2,5,12);
$list = array();
# Extract All Unique Conbinations
extractList($array, $list);
#Filter By SUM = $sum $list =
array_filter($list,function($var) use ($sum) { return(array_sum($var) == $sum);});
#Return Output
var_dump($list);
Output
array
0 => array
1 => string '1' (length=1)
2 => string '2' (length=1)
3 => string '3' (length=1)
4 => string '6' (length=1)
1 => array
1 => string '1' (length=1)
2 => string '5' (length=1)
3 => string '6' (length=1)
2 => array
1 => string '1' (length=1)
2 => string '11' (length=2)
3 => array
1 => string '12' (length=2)
Functions Used
function extractList($array, &$list, $temp = array()) {
if(count($temp) > 0 && ! in_array($temp, $list))
$list[] = $temp;
for($i = 0; $i < sizeof($array); $i ++) {
$copy = $array;
$elem = array_splice($copy, $i, 1);
if (sizeof($copy) > 0) {
$add = array_merge($temp, array($elem[0]));
sort($add);
extractList($copy, $list, $add);
} else {
$add = array_merge($temp, array($elem[0]));
sort($add);
if (! in_array($temp, $list)) {
$list[] = $add;
}
}
}
}
My answer
The code below uses the code referenced above. I changed the return functionality of the array_filter function to map it to your needs.
The only thing left for you to do is change the function so that it can catch multiple of the same type of room. At the moment, the code below will only output 1 of each type of room (as per the code referenced above). An easy way to get around this would be to multiply the array values you send to the function by the number of guests you are searching for rooms, but up to the amount of rooms available. So: if you are looking to book for 4 guests and you have no single rooms remaining and only 1 double room, your best match result would have to be a 2 person room and a 3 person room. I've added some brief functionality to add this (it's commented out), although I have not tested it. It will likely take a while to process that as well so if you're looking for a quicker method, you're gonna have to use a better algorithm as already mentioned in previous comments/answers or solve P vs NP
The code below also gives you the option to toggle a value of $exact. This value, if set to true, will return only matches exactly equal to the number of guests, and if set to false will return all matches that equal to at least the number of guests.
<?php
class Booking {
private $minGuests = 1;
protected $guests = 1;
protected $rooms = [];
public function getRoomCombinations(bool $exact = true) {
$guests = $this->guests;
$list = [];
$rooms = $this->rooms;
/*for($i = 0; $i < $guests-1; $i++) {
$rooms = array_merge($rooms, $this->rooms);
}
asort($rooms);*/
$this->extractList($rooms, $list);
$result = array_filter($list, function($var) use ($guests, $exact) {
if($exact)
return(array_sum(array_map(function($item) { return $item['room_for'];}, $var)) == $guests);
else
return(array_sum(array_map(function($item) { return $item['room_for'];}, $var)) >= $guests && count($var) <= $guests);
});
array_multisort(array_map('count', $result), SORT_ASC, $result);
return $result;
}
private function extractList(array $array, array &$list, array $temp = []) {
if (count($temp) > 0 && !in_array($temp, $list))
$list[] = $temp;
for($i = 0; $i < sizeof($array); $i++) {
$copy = $array;
$elem = array_splice($copy, $i, 1);
if (sizeof($copy) > 0) {
$add = array_merge($temp, array($elem[0]));
sort($add);
$this->extractList($copy, $list, $add);
} else {
$add = array_merge($temp, array($elem[0]));
sort($add);
if (!in_array($temp, $list)) {
$list[] = $add;
}
}
}
}
public function setGuests(int $guests) {
$this->guests = ($guests >= $this->minGuests ? $guests : $this->minGuests);
return $this;
}
public function setHotelRooms(array $rooms) {
$this->rooms = $rooms;
return $this;
}
}
$booking = (new Booking())
->setGuests(4)
->setHotelRooms([
[
"title" => "1 person room",
"room_for" => 1,
"price" => 79
],
[
"title" => "2 person room with other",
"room_for" => 1,
"price" => 69
],
[
"title" => "2 person room alone",
"room_for" => 1,
"price" => 89
],
[
"title" => "2 person",
"room_for" => 2,
"price" => 69
],
[
"title" => "3 person",
"room_for" => 3,
"price" => 69
]
]);
echo '<pre>' . var_export($booking->getRoomCombinations(true), true) . '</pre>';
?>
If you need all the combinations then you can use an backtracking iterative algorithm (depth path).
In summary:
Type of tree: binary tree because all the levels can contain a solution when the number of persons contabilized = objetive
Binary tree
Algorithm functions
You need to increment the cont every time that a level is generated with the number of persons of the level and decrement when you change your track (exploring brothers or back)
solution: array[0..levels-1] values {0 (node not selected) ,1 (node selected)}
solution[0] = 1 -> You choose that "1 person room" belongs to the solution
solutions: list/array of objects and every object contains array of titles of rooms
function Backtracking ()
level:= 1
solution:= s_initial
end:= false
repeat
generate(level, solution)
IF solution(level, solution) then
save_solution
else if test(level, solution) then
level:= level+ 1
else
while NOT MoreBrothers(level, solution)
go_back(level, s)
until level==0
2.1. Generate: generate next node
2.2. Solution: test if it's a solution
2.3. Critery: if we must continue by this track or bound
2.4. MoreBrothers: if there are nodes without check at this level
2.5. Backtrack: all the nodes at this level were explored
2.6. Save solution: add to the solutions array your object that contains strings
$room_array = array(
array(
"title" => "1 person room",
"room_for" => 1,
"price" => 79
),
array(
"title" => "2 person room with other",
"room_for" => 1,
"price" => 69
),
array(
"title" => "2 person room alone",
"room_for" => 1,
"price" => 89
),
array(
"title" => "2 person",
"room_for" => 2,
"price" => 69
),
array(
"title" => "3 person",
"room_for" => 3,
"price" => 69
)
);
// Gets rooms based on a given number of guests
function get_possible_rooms($num_guests) {
global $room_array;
$possible_rooms = [];
foreach ($room_array as $room) {
if ($num_guests <= $room['room_for']) {
$possible_rooms[] = $room['title'];
}
}
return $possible_rooms;
}
// Gets the available room capacities
function get_room_capacities() {
global $room_array;
$capacities = [];
foreach ($room_array as $room) {
$capacities[] = $room['room_for'];
}
return array_unique($capacities);
}
// Gets the different combinations of groups of guests based on the room capacities
function get_guest_assignments($remaining_guests, $parent_id = '', $num_guests, &$result) {
$room_capacities = get_room_capacities();
for ($i = 1; $i <= $remaining_guests; ++$i) {
if (in_array($i, $room_capacities)) {
$parent_guests = (isset($result[$parent_id])) ? $result[$parent_id] : 0;
$result[$parent_id . $i] = $parent_guests + $i;
for ($j = 1; $j <= $remaining_guests - $i; ++$j) {
// Recursively get the results for children
get_guest_assignments($j, $parent_id . $i, $num_guests, $result);
}
}
}
if ($remaining_guests === 1 && $parent_id !== '') {
// If it reaches the end and it does not fulfill the required number of guests,
// mark it for removal later
if ($result[$parent_id] < $num_guests) {
$result[$parent_id] = null;
}
}
// This is the last recursion
if ($result[$parent_id . '1'] === $num_guests) {
// Remove duplicates.
// To do this, we need to re-sort the keys (e.g. 21 becomes 12) and call array_unique()
// I admit this is a bit sloppy implementation.
$combinations = [];
foreach ($result as $key => $value) {
if ($value !== null) {
$nums = str_split($key);
sort($nums);
$combinations[] = implode('', $nums);
}
}
$result = array_unique($combinations);
}
}
// Gets the rooms for each group of guest
function get_room_assignments($guest_str) {
$rooms = [];
for ($i = 0; $i < strlen($guest_str); ++$i) {
$num_guests = intval(substr($guest_str, $i, 1));
$rooms[] = get_possible_rooms($num_guests);
}
return $rooms;
}
//----------
// RUN
//----------
$guests = 4;
$result = [];
get_guest_assignments($guests, null, $guests, $result);
foreach ($result as $guest_combi) {
$assignments = get_room_assignments($guest_combi);
// Printing output
echo 'Guest Combination ' . $guest_combi . "\n";
echo json_encode($assignments, JSON_PRETTY_PRINT);
echo "\n\n";
}
The output will look something like this:
...
Guest Combination 13
[
[
"1 person room",
"2 person room with other",
"2 person room alone",
"2 person",
"3 person"
],
[
"3 person"
]
]
...
"Guest combination 13" means the 4 guests will be split into groups of 1 and 3 persons.
Output is an array of possible rooms for each group. So in the example, the group of 1 can book 1 person room, 2 person room with other, ... 3 person room. And the group of 3 can book 3 person room.
—
Other notes:
I know we hate global but doing this just for brevity. Feel free to modify.
There's a shorter way to code this, but this implementation makes it easier to debug since guest combinations are used as keys.
How can I sort an array with some posts from a bank account?
I need to sort by three fields: date, amount, accumulated amount
ie.
date | amount | accum_amount
01-01-11 500 500 ('amount' + previous 'accum_amount' = 'accum_amount' => 500 + 0 = 500)
01-01-11 100 600 ('amount' + previous 'accum_amount' = 'accum_amount' => 100 + 500 = 600)
01-02-11 -25 575 ('amount' + previous 'accum_amount' = 'accum_amount' => -25 + 600 = 575)
01-02-11 150 725 ('amount' + previous 'accum_amount' = 'accum_amount' => 150 + 575 = 725)
01-03-11 200 925 ('amount' + previous 'accum_amount' = 'accum_amount' => 200 + 725 = 925)
01-04-11 -25 900 ('amount' + previous 'accum_amount' = 'accum_amount' => -25 + 925 = 900)
btw. the date field is an UNIX timestamp
array(
array(
'time' => 1200000000,
'amount' => 500,
'accum_amount' => 500
),
array(
'time' => 1200000000,
'amount' => 150,
'accum_amount' => 725
),
array(
'time' => 1200000000,
'amount' => 100,
'accum_amount' => 600
),
array(
'time' => 1300000000,
'amount' => 200,
'accum_amount' => 925
),
array(
'time' => 1300000000,
'amount' => -25,
'accum_amount' => 900
),
array(
'time' => 1200000000,
'amount' => -25,
'accum_amount' => 575
)
)
Change your array structure to something like:
$tmp = array(
time => array(1200000000, 1200000000, 1200000000, ...),
amount => array(500, 150, 100, ...),
accum_amount => array(500, 725, 600, ...),
)
and then use array_multisort for sorting (http://php.net/manual/en/function.array-multisort.php) like:
array_multisort($tmp['time'], SORT_ASC, $tmp['amount'], SORT_DESC, $tmp['accum_amount'], SORT_ASC);
It's not very intuitive, but it should work.
Of course you can write a helper function to map the new sorting order on other array structure.
Example:
function mySort($array, $sort) {
$tmp = array();
foreach ($array as $row) {
foreach ($row as $field => $value) {
if (!isset($tmp[$field])) {
$tmp[$field] = array();
}
$tmp[$field][] = $value;
}
}
$params = array();
foreach ($sort as $field => $direction) {
$params[] = $tmp[$field];
$params[] = $direction;
}
$keys = array_keys($array);
$params[] =& $keys;
call_user_func_array('array_multisort', $params);
$result = array();
foreach ($keys as $key) {
$result[$key] = $array[$key];
}
return $result;
}
Call:
$data = mySort($data, array(
'time' => SORT_ASC,
'amount' => SORT_ASC,
'accum_amount' => SORT_ASC
));
function usort() is made for this purpose:
usort($data, "my_sort");
function my_sort($row1, $row2) {
if ($row1['time']<$row2['time']) return -1;
if ($row1['time']>$row2['time']) return 1;
if ($row1['amount']<$row2['amount']) return -1;
if ($row1['amount']>$row2['amount']) return 1;
if ($row1['accum_amount']<$row2['accum_amount']) return -1;
if ($row1['accum_amount']>$row2['accum_amount']) return 1;
return 0;
}
Your example isnt really clear what you want. You don't describe it this way, but from example it appears you want to sort by date increasing, then accum_amount increasing, then amount (increasing?)
You just have to write a comparison function and pass it to uasort along with your array
$comp = function($a,$b)
{
if ($a['date'] != $b['date'])
return $a['date'] - $b['date'];
else if ($a['accum_amount'] != $b['accum_amount'])
return $a['accum_amount'] - $b['accum_amount'];
return $a['amount'] - $b['amount'];
}
uasort($arr, $comp);
You might find a function like this helpful. It will create the comparison function when given an array, in order of importance, of the keys to compare by. (All ascending, support for descending would necessarily complicate it a good deal)
$compMakerAsc = function($l)
{
return function($a, $b) use ($l)
{
foreach($l as $k)
{
if ($a[$k] != $b[$k])
return $a[$k] - $b[$k];
//OR return ($a[$k] > $b[$k]) ? 1 : -1;
}
return 0;
}
$myComp = $compMakerAsc('date', 'accum_amount', 'amount');
$uasort($arr, $myComp);
$key = 'accum_amount';
function sortme($a, $b)
{
global $key;
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] < $b[$key]) ? -1 : 1;
}
usort($array, "sortme");
perhaps this should help. Bad thing, have to define global variable. Complete code!!!
$array = array(
array(
'time' => 1200000000,
'amount' => 500,
'accum_amount' => 500
),
array(
'time' => 1200000000,
'amount' => 150,
'accum_amount' => 725
),
array(
'time' => 1200000000,
'amount' => 100,
'accum_amount' => 600
),
array(
'time' => 1300000000,
'amount' => 200,
'accum_amount' => 925
),
array(
'time' => 1300000000,
'amount' => -25,
'accum_amount' => 900
),
array(
'time' => 1200000000,
'amount' => -25,
'accum_amount' => 575
)
);
$key = 'accum_amount';
function sortme($a, $b)
{
global $key;
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] < $b[$key]) ? -1 : 1;
}
usort($array, "sortme");
echo "<pre>";
print_r($array);
echo "</pre>";
Let's combine this from the comments
the query is just as simple as SELECT time, amount, accum_amount FROM table
with this requirement from the original question:
I need to sort by three fields: date, amount, accumulated amount
Now, there were no requirements stated as to whether the sort is ascending or not. Let's assume ascending.
SELECT time, amount, accum_amount
FROM table
ORDER BY time ASC, amount ASC, accum_amount ASC
This will give us all of the data first sorted by time. When the time is identical, it will sort second by amount, lowest first and highest last. When the time and amount are identical, it will sort third by the accumulated amount, again sorted lowest first, highest last.
Changing the order of operations with the sort is as simple as swapping around the list in the ORDER BY clause. Going from largest to smallest is as easy as switching from ASC to DESC.
Note how relatively easy this method is in comparison to sorting in PHP. Whenever you can, let your database give you pre-sorted results.
Because I need to leave the site for a few hours, I'm just going to type this up real quick. I'm operating right now under the assumption that you want to recalculate the accumulated value.
Our query is now:
SELECT time, amount
FROM table
ORDER BY time ASC, amount DESC
We'll be a friendly bank and perform all deposits (increases in amount) first, then all debits (decreases in amount) last, processing the largest debits later than the smaller debits.
Given $data is our array:
$running_total = 0;
foreach($data as $index => $row) {
$data[$index]['accum_amount'] = $running_total + $row['amount'];
$running_total = $data[$index]['accum_amount'];
}
This should successfully rebuild the accum_amount list, given the business rules I have outlined above.
I have found another way to do it before the data is put into the db