I am trying to work out how to correct display the rank of points to show a leaderboard.
I currently have the following in my Modal
public function getPointrankAttribute(){
$year = Carbon::parse(Carbon::now())->year;
$pointrank = Points::query()
->select('member_id')->selectRaw('SUM(value) as TotalPoints')
->where('Year','=', $year)
->groupBy('member_id')
->orderByDesc('Totalpoints')
->get();
return $pointrank->search(function($points){
return $points->member_id == $this->id;
}) + 1;
}
This works well, however it is using the row position in the table to show the rank, so the first record is 1st and the 2nd record shows as 2nd.
However the issue arises when members have the same total points, because under this the rank is based on position on in the table
See the following example
|member_id|totalpoints|
| 12 | 20 |
| 3 | 10 |
| 10 | 10 |
| 5 | 5 |
In this example based on my current solution
Member 12 = 1st, Member 3 = 2nd, Member 10 = 3rd, Member 5 = 4th
What I want is the following
Member 12 = 1st, Member 3 = 2nd, Member 10 = 2nd, Member 5 = 4th
It would be awesome if I could also add "=" as well when we have more than 1 member sharing that position (as in Member 3 and 10 in this case)
Thanks in advance
To get rank for your users you will need to assign rank to distinct total points first, using laravel collection helper you can create separate ranks collection irrespective of member ids like
$collection =collect(
[
[
"member_id" => 12,
"totalpoints" => 20
],
[
"member_id" => 3,
"totalpoints" => 10
],
[
"member_id" => 10,
"totalpoints" => 10
],
[
"member_id" => 5,
"totalpoints" => 5
],
]
);
$ranks = $collection->unique('totalpoints')
->values()
->mapWithKeys(function ($item, $index) {
return [$item['totalpoints'] => $index + 1];
});
Make sure the original collection should be ordered based on total points.
The above will create an array of distinct points as keys and values as ranks based on total points like
Array
(
[20] => 1
[10] => 2
[5] => 3
)
Now you can search your members based on id from original collection and get their respective rank from ranks array like
$id = 10;
$ranks[$collection->firstWhere("member_id", $id)["totalpoints"]];
There is another way to get ranks for your records directly from database if the vendor database supports window functions like RANK()
What I want is the following :Member 12 = 1st, Member 3 = 2nd, Member 10 = 2nd, Member 5 = 4th
To achieve your desired results you just need to remove ->values() from above code so that your results are not re indexed
$ranks = $collection->unique('totalpoints')
->mapWithKeys(function ($item, $index) {
return [$item['totalpoints'] => $index + 1];
});
Array
(
[20] => 1
[10] => 2
[5] => 4
)
DEMO
Related
I have a membership that for any given day, can only have 3 users assigned to it.
user
start_date
end_date
User1
2023-01-01
2023-01-30
User2
2023-01-01
2023-01-25
User3
2023-01-26
2023-01-30
This set of records would be fine because on any given day there is not more than 3 users. My issue arises when I try to add:
user
start_date
end_date
User4
2023-01-01
2023-01-30
Using SELECT * FROM members WHERE start_date >= '2023-01-01' AND end_date <= '2023-01-30' returns 3 records like it should. I'm having a hard time trying to figure out how to make sure that none of these overlap to exceed 3.
I'm after advice on how I can achieve this using php, but pseudocode would work too
Loop over the date range in each result record, and count how many entries you got for any given date in an array. If you only need the maximum number of overlapping days, simply get the maximum of the array values. (If you need to know the specific dates where it would go over the limit - then you can loop the array.)
$rows = [
['2023-01-01', '2023-01-30'],
['2023-01-01', '2023-01-25'],
['2023-01-26', '2023-01-30'],
];
$countByDates = [];
foreach($rows as $row) { // this is going to be a classic while loop
// over your database result in your actual use case
$start = strtotime($row[0]); // access rather by column name than array index
$end = strtotime($row[1]);
do {
$date = date('Y-m-d', $start);
$countByDates[$date] = ($countByDates[$date] ?? 0) + 1;
$start = strtotime('+1 day', $start);
} while($start <= $end);
}
print_r($countByDates);
echo "maximum number of daily bookings: ", max($countByDates);
Result:
Array
(
[2023-01-01] => 2
[2023-01-02] => 2
[2023-01-03] => 2
[2023-01-04] => 2
[2023-01-05] => 2
[2023-01-06] => 2
[2023-01-07] => 2
[2023-01-08] => 2
[2023-01-09] => 2
[2023-01-10] => 2
[2023-01-11] => 2
[2023-01-12] => 2
[2023-01-13] => 2
[2023-01-14] => 2
[2023-01-15] => 2
[2023-01-16] => 2
[2023-01-17] => 2
[2023-01-18] => 2
[2023-01-19] => 2
[2023-01-20] => 2
[2023-01-21] => 2
[2023-01-22] => 2
[2023-01-23] => 2
[2023-01-24] => 2
[2023-01-25] => 1
[2023-01-26] => 2
[2023-01-27] => 2
[2023-01-28] => 2
[2023-01-29] => 2
[2023-01-30] => 2
)
maximum number of daily bookings: 2
https://3v4l.org/1W3WD
I have trouble with some calculation in Codeigniter. Please check attached image below
https://imgur.com/HuSBkXJ
That data fetching from the database , the sale column is fine but I having trouble with monthly sale
1st day of every month sale (10) and monthly sale (10) are same, but from second day sale is correct(8), monthly sale has to be 1st day sale + second day sale(10+8 = 18). for third day sale is fine, monthly sale should be (18+20=38). the calculation going like this up to end of the month, Same process again stating from next month
How do we calculate in codeigniter, The above image i have created in excel sheet for demonstration purpose.
Model view
public function fetchSaleData()
{
$this->db->join('stock_shop','stock_shop.shop_id = stock_sale.shopId');
$this->db->join('stock_products','stock_products.product_id = stock_sale.productID');
$thisMonth=date('m');
$this->db->where('MONTH(DateOfSale)',$thisMonth);
$query= $this->db->get("stock_sale");
return $query->result_array();
}
controller view
public function salesView()
{
$data['sales']=$this->Query_sale->fetchSaleData();
$data['shops']=$this->Query_sale->fetchShop();
$this->load->view('stock/shop_sales_view',$data);
}
since you're not showing table structures or any other insight, I'll only suggest an approach based on my own code which you may feel free to adapt to your specific needs, along with the assumptions I make along the way
Assumption #1: the model returns an array in which each element contains the date and the sold units on such date
This would look like this
+-----------------+
| array data |
+------------+----+
| 2020-01-01 | 10 |
+------------+----+
| 2020-01-02 | 20 |
+------------+----+
| 2020-01-03 | 5 |
+------------+----+
| 2020-01-04 | 8 |
+------------+----+
On array form, this may look like this:
$ar = array(
0 => array(
'date' => '2020-01-01',
'units_sold' => 10,
),
1 => array(
'date' => '2020-01-02',
'units_sold' => 20,
),
2 => array(
'date' => '2020-01-03',
'units_sold' => 5,
),
3 => array(
'date' => '2020-01-04',
'units_sold' => 8,
),
);
and so on until the end of the month. Lets call the returned fields date and units_sold as in the above example
now that you have the data loaded in a variable ($ar) you can loop through it and keep tabs on the accumulated total on each loop.
$accum = 0; // keep track of accumulated total
$i = 0; // keep track of array index
foreach ($ar as $a)
{
$accum += $a['sales'];
$ar[$i]['accum'] = $accum;
$i++;
}
Upon running, this code will:
Iteration 0: For 2020-01-01, sales=10, accumulated=10
Iteration 1: For 2020-01-02, sales 20, accumulated=30
Iteration 2: For 2020-01-03, sales 5, accumulated=35
...and so on until the month is over
The above can be verified with a print_r or var_dump (I use print_r... like this: print print_r($ar,true);):
print_r output
Array
(
[0] => Array
(
[date] => 2020-01-01
[sales] => 10
[accum] => 10
)
[1] => Array
(
[date] => 2020-01-02
[sales] => 20
[accum] => 30
)
[2] => Array
(
[date] => 2020-01-03
[sales] => 5
[accum] => 35
)
[3] => Array
(
[date] => 2020-01-04
[sales] => 8
[accum] => 43
)
)
So, now that you've succesfully pushed a whole new element in your result array, you can send it over to the view and display it in tabular form as you need
I have a data that looks like this
"discountPrices": [
{
"startQuantity": 3,
"price": 65.0
},
{
"startQuantity": 8,
"price": 62.0
},
{
"startQuantity": 20,
"price": 60.0
},
]
As you know, this is a discount price of a product. I am trying to achieve two things here.
1.) I want to create a table called discountprice and the table will have columns namely price | minqty | maxqty. Using the data above, the first row will be 65.00 | 3 | 7 , and the second row will be 62.00 | 8 | 19 and the third row will be 60 | 20 | 1000.
The issue I am first here is that I don't know how to get the maximum qty because the data did not contain the maximum quantity. It only states the startQuantity. How can I get the Maxquanity for each of the row and record it?
2.) How can I output it on a page. For example, using the data, I can do something like 3>=7 = $65.00, 8>=19 = $62.00 , 20>=1000 = $60.00
I think the hardest part is how to know the maximum quantity since it is not specified. Please how can I achieve this result.
You don't need to know the max qty. Just store your data as is and query for the first row that has a startQuantity less than or equal to your orderQuantity:
SELECT price FROM discounts
WHERE startQuantity <= :orderQuantity
ORDER BY startQuantity DESC
LIMIT 1
See here for example.
If you don't find a result, then there's no discount and your price is just the base undiscounted price. (Or you could insert a row with the base price and 0 for startQuantity.)
Note, I assume you're selling more than one product, so you'll likely need a field in that table (and the associated WHERE clause) to identify which product.
You can use the following code snnipt to get the max quantity
$json = '[
{
"startQuantity":3,
"price":65.0
},
{
"startQuantity":8,
"price":62.0
},
{
"startQuantity":20,
"price":60.0
}
]';
$jsonToArray = json_decode($json, true);
$startQuantity = array_column($jsonToArray, 'startQuantity');
array_walk($jsonToArray, function($v, $k) use (&$jsonToArray, $startQuantity) {
$index = array_search($v['startQuantity'], $startQuantity);
if(array_key_exists($index+1, $startQuantity))
$jsonToArray[$k]['endQuantity'] = $startQuantity[$index+1]-1;
});
print_r($jsonToArray);
Result
Array
(
[0] => Array
(
[startQuantity] => 3
[price] => 65
[endQuantity] => 7
)
[1] => Array
(
[startQuantity] => 8
[price] => 62
[endQuantity] => 19
)
[2] => Array
(
[startQuantity] => 20
[price] => 60
)
)
I am building a badges app in CodeIgniter using a MySQL database. There are 3 types of badges: level 1, level 2 and level 3 badges.
Each level 2 badge is awarded once you get all the required level 1 badges and each level 3 badge is awarded once you get all the required level 1 and level 2 badges. There is also a level 3 badge that requires getting 4 level 3 badges.
I have the following database table for my badges relations:
badge_id - children_id
1 - 20
1 - 25
20 - 40
20 - 45
26 - 40
25 - 39
40 - 50
I need a function that returns me all the ancestors of a badge.
For example, if the function recieved the argument 50 it would return: 40, 26, 20, 1. Any ideas?
If you want to handle it by pure mysql, you need to declare a mysql stored procedure which has some depth limitations.
Instead of that I would recommend using a simple recursive PHP function to return what you need:
function get_ancestors($child_id, $badges)
{
$found = array();
foreach($badges as $k => $row){
if ($row['children_id'] == $child_id){
$found = array_merge($found, get_ancestors($row['badge_id'], $badges));
$found[] = $row['badge_id'];
}
}
return $found;
}
The $badges variable holds all the rows from your badges relations table, just simply read it out from the database.
Based on the example you gave, for the following call
get_ancestors(50, $badges);
the output would be:
Array
(
[0] => 1
[1] => 20
[2] => 26
[3] => 40
)
Original data looks like this: banners/ad_1.png | Banner ad 1 | 1
Here is an array using the print_r function on it:
Array ( [0] => banners/ad_1.png Banner ad 1 1
[1] => banners/ad_2.png Banner ad 2 2
[2] => banners/ad_3.png Banner ad 3 3 )
This is after exploding it with a | delimiter, so it's separated by img src, alt text, num times viewed.
Is there a way I can return the banner information by num times viewed, max or min?
I have been playing with min, max, array_values, array_keys, array_multisort.. I can't figure it out.
Thanks!
This should work, provided this array doesn't get so big that it eats up significant chunks of memory:
<?php
$array = array(
'banners/ad_1.png | Banner ad 1 | 1',
'banners/ad_2.png | Banner ad 2 | 2',
'banners/ad_3.png | Banner ad 3 | 3'
);
$sort = array();
foreach ($array as $row)
{
$row = explode(" | ", $row); // split up string into a format we can deal with
// use a unique index so we can keep track of association
$idx = trim($row[0]);
$sort[$idx] = trim($row[2]);
}
arsort($sort); // sorts by value in descending order, keeps index association
print_r($sort);
/*
Should be:
Array(
'banners/ad_3.png' => 3,
'banners/ad_2.png' => 2,
'banners/ad_1.png' => 1
)
*/
Here's some documentation on the arsort function I used.