I perform a series of operations (originally using underscore) on an array of objects that found the sales value for the month with the highest sales.
The data: The values array represents the total sales for a given month. x = date, y = sales.
[
{
name:'banana',
values:[
{
x: //date object: jan 2016,
y: 3423
},
{
x: //date object: feb 2016,
y: 53453
},
{
x: //date object: mar 2016,
y: 65457
},
{
x: //date object: apr 2016,
y: 456345
},
...//12 months
]
},
{
name:'apple',
values:[
{
x: //date object: jan 2016,
y: 34876
},
{
x: //date object: feb 2016,
y: 89
},
{
x: //date object: mar 2016,
y: 7896
},
{
x: //date object: apr 2016,
y: 6547
},
...//12 months
]
},
... more objects
]
My function: Returns the highest sales amount out of all months.
getMaxMonthlySales:function(data){
var salesArrays = _.map(data,obj=>_.map(obj.values,'y'))
var maxSales = _.zip.apply(null,salesArrays)
maxSales = _.reduce(maxSales,(max,arr)=>{
var sum = _.reduce(arr,(memo,val)=>memo+val);
return Math.max(max,sum);
},0)
return maxSales;
}
All values arrays per object are guaranteed to be the same length and have the same order of dates. My general strategy is to perform a nested map to obtain an array of arrays of only sales. Then zip the arrays to group sales by month. Then finally reduce each month to find the total sales per month, then find the maximum month.
My question is how can i rewrite this function as ideally a lodash chain?
As a oneliner:
getMaxMonthlySales: function(data) {
return _.max(_.map(_.groupBy(_.flatten(_.map(data, 'values')), 'x'),
o => _.sumBy(o, 'y')));
}
Or
getMaxMonthlySales: function(data) {
return _(data)
.map('values')
.flatten()
.groupBy('x')
.map(o => _.sumBy(o, 'y'))
.max();
}
Related
I want to convert the following SQL query to MongoDB query:
SELECT count(invoiceNo), year, month, manager
FROM battle
WHERE year=2021 AND month='Dec' OR year=2022 AND month='Jan' AND manager = 'name#test.com'
GROUP BY year,month;
I've tried to do so, but it seems to be incorrect:
const getNoOfOrders = await BattlefieldInfo.aggregate([
{
$match: {
$and: [
{
year: periodDate[0]['year']
},
{ month: periodDate[0]['month'] }
],
$or: [
{
$and: [
{
year: prevYear
},
{ month: prevMonth }
]
}
],
$and: [{ manager: email }]
}
},
{
$group: {
_id: '$month'
}
},
{
$project: {
// noOfOrders: { $count: '$invoiceNo' },
month: 1,
year: 1,
manager: 1
}
}
]);
Because I am getting an empty array. But it should be something like this:
| count(invoiceNo) | manager | year | month |
+------------------+---------------+------+-------+
2 name#test.com 2021 Dec
3 name#test.com 2022 Jan
From my point of view, I think parenthesis (bracket) is important to group the conditions together such as month and year.
SELECT count(invoiceNo), `year`, month, manager
FROM battle
WHERE (`year` = 2021 AND month = 'Dec')
OR (`year` = 2022 AND month = 'Jan')
AND manager = 'abc#email.com'
GROUP BY month, `year`
Sample DBFiddle
Same goes for your MongoDB query. While to search with month and year, you can do without $and as below:
{
year: 2021,
month: "Dec"
}
Instead of:
$and: [
{
year: 2021
},
{
month: "Dec"
}
]
And make sure that $group stage need an accumulator operator:
noOfOrders: {
$count: {}
}
Or
noOfOrders: {
$sum: 1
}
Complete MongoDB query
db.collection.aggregate([
{
$match: {
$or: [
{
year: 2021,
month: "Dec"
},
{
year: 2022,
month: "Jan"
}
],
manager: "abc#email.com"
}
},
{
$group: {
_id: {
month: "$month",
year: "$year"
},
noOfOrders: {
$count: {}
},
manager: {
$first: "$manager"
}
}
},
{
$project: {
_id: 0,
noOfOrders: 1,
month: "$_id.month",
year: "$_id.year",
manager: "$manager"
}
}
])
Sample Mongo Playground
Note:
Would be great for both queries to add manager as one of the group keys. Since you are filtering for the specific (only one) manager's record(s), it's fine. But without filtering for specific manager, your query will result in the wrong output.
I have a tasks in BigQuery with created date and last modified date. I would like to be able to report the number of task open and task close events by date in the same table if possible.
view: tasks {
derived_table: {
sql:
SELECT *
FROM UNNEST(ARRAY<STRUCT<CREATED_DATE DATE, LAST_MODIFIED DATE, ID INT64, STATE STRING>>[
('2020-12-01', '2020-12-01', 1, "OPEN"),
('2020-12-01', '2020-12-03', 2, "CLOSED"),
('2020-12-02', '2020-12-03', 3, "CLOSED"),
('2020-12-03', '2020-12-05', 4, "OPEN"),
('2020-12-05', '2020-12-05', 5, "CLOSED")])
;;
}
dimension_group: created {
type: time
datatype: date
sql: ${TABLE}.created_date ;;
}
dimension_group: last_modified {
type: time
datatype: date
sql: ${TABLE}.last_modified ;;
}
dimension: id {
type: number
}
dimension: state {
type: string
}
measure: number_of_tasks {
type: count_distinct
sql: ${id} ;;
}
measure: number_of_open_tasks {
type: count_distinct
sql: ${id} ;;
filters: {
field: "state"
value: "OPEN"
}
}
measure: number_of_closed_tasks {
type: count_distinct
sql: ${id} ;;
filters: {
field: "state"
value: "CLOSED"
}
}
}
explore: tasks {}
I can get the number of opened tasks using the created date.
I can get the number of tasks closed by counting tasks, where the last modified date is in the aggregating period and status is closed, with a filtered measure.
However, if I try to combine these in a single table I get a row for each combination of dates.
How can I count task state changes by date?
Date
Number of Opened Tasks
Number of Closed Tasks
2020-12-01
2
0
2020-12-02
1
0
2020-12-03
1
2
2020-12-04
0
0
2020-12-05
1
1
A colleague has suggested a solution. Stacking the tasks table on itself creates (up to) two rows per task.
view: tasks {
derived_table: {
sql:
WITH tab AS (
SELECT *
FROM UNNEST(ARRAY<STRUCT<CREATED_DATE DATE, LAST_MODIFIED DATE, ID INT64, STATE STRING>>[
('2020-12-01', '2020-12-01', 1, "OPEN"),
('2020-12-01', '2020-12-03', 2, "CLOSED"),
('2020-12-02', '2020-12-03', 3, "CLOSED"),
('2020-12-03', '2020-12-05', 4, "OPEN"),
('2020-12-05', '2020-12-05', 5, "CLOSED")])
)
SELECT *, 1 open_count, 0 closed_count, created_date AS action_date
FROM tab
UNION DISTINCT
SELECT *, 0 open_count, 1 closed_count, last_modified AS action_date
FROM tab
WHERE state = "CLOSED"
;;
}
dimension_group: created {
type: time
datatype: date
sql: ${TABLE}.created_date ;;
}
dimension_group: last_modified {
type: time
datatype: date
sql: ${TABLE}.last_modified ;;
}
dimension_group: action {
type: time
datatype: date
sql: ${TABLE}.action_date ;;
}
dimension: id {
type: number
}
dimension: state {
type: string
}
dimension: open_count {
type: number
hidden: yes
}
dimension: closed_count {
type: number
hidden: yes
}
measure: number_opened{
type: sum
sql: ${open_count} ;;
}
measure: number_closed {
type: sum
sql: ${closed_count} ;;
}
}
explore: tasks {}
The opened and closed tags can then be counted.
I have a sample array of objects...
const entries = [
{
userID: 1,
date: '2019/06/15',
numSteps: 3577,
minutesActive: 140,
flightsOfStairs: 16
},
{
userID: 1,
date: '2019/06/16',
numSteps: 6637,
minutesActive: 175,
flightsOfStairs: 36
},
{
userID: 1,
date: '2019/06/17',
numSteps: 14329,
minutesActive: 168,
flightsOfStairs: 18
},
{
userID: 1,
date: '2019/06/18',
numSteps: 4419,
minutesActive: 165,
flightsOfStairs: 33
},
{
userID: 1,
date: '2019/06/19',
numSteps: 8429,
minutesActive: 275,
flightsOfStairs: 2
},
{
userID: 1,
date: '2019/06/20',
numSteps: 14478,
minutesActive: 140,
flightsOfStairs: 12
},
{
userID: 1,
date: '2019/06/21',
numSteps: 6760,
minutesActive: 135,
flightsOfStairs: 6
},
{
userID: 1,
date: '2019/06/22',
numSteps: 10289,
minutesActive: 119,
flightsOfStairs: 6
},
{
userID: 1,
date: '2019/06/23',
numSteps: 8213,
minutesActive: 122,
flightsOfStairs: 27
},
{
userID: 1,
date: '2019/06/24',
numSteps: 11654,
minutesActive: 270,
flightsOfStairs: 19
}
]
I am trying to find instances of increasing numSteps through a for loop, so if there are consecutive days of increasing steps, those days get pushed into a new array. This is my loop...
let dates = []
for (var i = 0; i < this.entries.length - 1; i++) {
if (this.entries[i].numSteps < this.entries[i + 1].numSteps) {
dates.push(this.entries[i])
}
}
I am very close to getting this to work, but can't figure out how to push the final value before the condition is met. I should be getting the first three index pushed into the new array, but am only getting two. I see why this is happening, because by the time I hit the third index, the condition in my loop is no longer true. I am just not sure how I can push the indexes that meet the condition and the one directly after that. Any suggestions? Thanks!
You need to make the for loop cover the full range, then enhance the if statement to compare with both next and previous.
let len = this.entries.length;
for (var i = 0; i < len ; i++) {
if ((i < len - 1 && this.entries[i].numSteps < this.entries[i + 1].numSteps)
|| (i > 0 && this.entries[i - 1].numSteps < this.entries[i].numSteps)) {
dates.push(this.entries[i])
}
}
I displays all the months even there is no data in every month. I use an SQL query to get all the months that have data and VueJS to get all the months with no data. The problem is all the months with data came first and the next one is the no data. The label looks like this April, May, June, July, August, January, February, March, September, October, November, December. How can I sort the months according to the calendar? Can somebody help me with my problem? Here's my code.
METHODS
retrieveDistributedPerMonthByLine : function() {
var self = this;
var months = ["January", 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
axios.post(this.urlRoot + this.api + "retrieve_transaction_per_month.php")
.then(response => {
// console.log("m");
console.log(response);
vm.transaction_per_month = response.data
for(var i = 0 ; i < months.length; i++) {
var isFound = false;
for(var j = 0 ; j < this.transaction_per_month.length; j++) {
if(this.transaction_per_month[j].Month == months[i]) {
isFound = true;
break;
}
}
if(!isFound) {
this.transaction_per_month.push({
Month: months[i],
Amount: 0,
Beneficiary: 0,
Quantity: 0
});
}
}
var ctxChart = self.$refs.myChart3.getContext('2d')
var myChart3 = new Chart(ctxChart, {
type: 'bar',
data: {
labels: vm.transaction_per_month.map(item => item.Month),
datasets: [{
label: 'Total Amount',
data: vm.transaction_per_month.map(item => item.Amount),
backgroundColor: this.poolColors(vm.distributed_per_day.length),
borderColor: '#eee',
borderWidth: 1
}]
},
reponsive: true,
options: {
title : {
display : true,
text : "Distributed Reports per Month",
fontFamily: "sans-serif",
fontSize: 18
},
legend: {
display: false
},
tooltips: {
enabled: true
}
}
});
}).catch(e => {
console.log(e)
});
},
SQL
public function getTransactionPerMonth() {
$sql="SELECT DATE_FORMAT(tbl_transaction.transaction_date, '%M')
AS
Month, SUM(tbl_transaction_details.total_price)
AS
Amount, SUM(tbl_transaction_details.quantity)
AS
Quantity, COUNT(tbl_transaction.beneficiary_id)
AS
Beneficiary
FROM
tbl_transaction_details
INNER JOIN
tbl_supplier_medicine ON tbl_transaction_details.supplier_medicine_id = tbl_supplier_medicine.supplier_medicine_id
INNER JOIN
tbl_transaction ON tbl_transaction.transaction_id = tbl_transaction_details.transaction_id
INNER JOIN
tbl_barangay ON tbl_barangay.barangay_id = tbl_transaction.barangay_id
WHERE
YEAR(tbl_transaction.transaction_date) = YEAR(NOW())
AND
tbl_transaction_details.total_price > 0
AND
tbl_barangay.barangay_id = $_SESSION[barangay_id]
GROUP BY
DATE_FORMAT(tbl_transaction.transaction_date, '%M')
ORDER BY
MAX(tbl_transaction.transaction_date)";
$stmt = $this->connection->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
}
Try this:
computed : {
sorted_orders_by_months() {
var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
return this.transaction_per_month.sort(function(a, b){
return months.indexOf(a.Month) - months.indexOf(b.Month.);
});
}
}
And then to represent it you can do something like:
v-for="(value, index) in sorted_orders_by_months"
I want to get years and apply group by on it to get the counts of record. For this my SQL query is -
select substr(lastModified,0,4) , count(*) from EOM group by substr(lastModified,0,4).
I created query for above is -
{
"size":0,
"aggs": {
"profit": {
"scripted_metric": {
"init_script" : "_agg.transactions = []",
"map_script" : "_agg.transactions.add(doc.timeModified.date.getYear())",
"combine_script" : "return _agg.transactions"
}
},
"aggs":{
"terms":{
"field":"profit"
}
}
}
}
RESULT -
{
"aggregations": {
"profit": {
"value": [
[
2014,
2015,
2016,
2015,
2017
],
[
2015,
2015,
2016,
2016,
2017
]
]
},
"aggs": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": []
}
}
}
It give me years but not apply aggrgation on profit and its bucket is blank.
I want sense query for the above sql query.
Why not use a Date Range aggregation instead of a scripted metric ?
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html
(the only drawback is that you will have to manually provide the years in ranges section)