How to do groupBy and add the sum of the values grouped in underscore.js - react-native

From this:
var x = [
{category: 'Title', price: 100 },
{category: 'Title', price: 200 },
{category: 'Title3', price: 300 },
{category: 'Title3', price: 400 },
{category: 'Title5', price: 500 },
];
To this:
var x = [
{category: 'Title', price: 300},
{category: 'Title3', price: 700},
{category: 'Title5', price: 500},
];
The logic is same as SQL query:
SELECT category, sum (price) FROM x GROUP BY category;

Updated:
groupBy will group the initial array by "category".
map will then iterate each object in the grouped array by passing in two params: key and value. The first key will be "Title" and its value is an array of [{category: 'Title', price: 100 }, {category: 'Title', price: 200 }].
Before returning an updated array in map, reduce is used to sum up the "price". Basically, it iterates through the array (first param) and return the sum from the function (second param). Note that in the first iteration, the total will be 0 (last param in reduce). So, the first return will be 0 + 100 which is then passed in as 'new' total in the second iteration and so on.
You can refer to the documentation for more details.
Try this:
var groups = _.groupBy(x, 'category');
var result = _.map(groups, function(value, key) {
return {
category: key,
price: _.reduce(value, function(total, o) {
return total + o.price;
}, 0)
};
});

Related

How to find if two arrays contain any common item in SQL? With one array being my set result and the other a list of 'ids'?

This is currently what my entities look like:
Category Entity
#Entity('category')
export class Category extends BaseEntity {
#PrimaryGeneratedColumn("uuid")
id: string;
#Column({ type: 'text', unique: true })
name: string;
#Column({ type: "text", unique: true })
#Index()
slug: string;
#ManyToMany(() => Listing, (listing) => listing.categories, { cascade: true, onDelete: 'CASCADE' })
listings?: Listing[];
}
Listing Entity
#Entity('listing')
export class Listing extends BaseEntity {
#PrimaryGeneratedColumn("uuid")
id: string;
#ManyToMany(() => Category, (category) => category.listings)
#JoinTable()
categories: Category[];
}
Query 1 (what I'm currently using)
And this is currently what my query looks like:
const listings = await connection.getRepository()
.createQueryBuilder('listing')
.distinct(true)
.leftJoinAndSelect('listing.categories', 'category', 'category.slug IN (:...slugs)', {slugs: [ 'mens-shirts', 'clearance' ]})
.getMany()
Query 1 Result
[] // an empty list of Listings (Type: Listing[])
Query 2 (checking to see if the innerJoinAndSelect was working properly)
const listings = await connection.getRepository()
.createQueryBuilder('listing')
.distinct(true)
.innerJoinAndSelect('listing.categories', 'category')
.getMany();
Query 2 Result
[
Listing {
id: 'c24ea98d-da53-4f14-8706-a3597f3ee4d1',
categories: [ [Category], [Category] ]
},
Listing {
id: 'e8b3e680-85b6-4701-9ad7-bf65de348e76',
categories: [ [Category], [Category] ]
},
Listing {
id: '1bb04ea0-8435-44d6-856f-8eb53f24e941',
categories: [ [Category], [Category] ]
},
Listing {
id: '0735142d-fd38-4fad-b5a7-0356373dd0a3',
categories: [ [Category], [Category] ]
},
]
The innerJoinAndSelect method is working and giving me the results back, and I know why I'm getting an empty array when using the first query. It's because I'm trying to find the field slug on the Array of Categories, instead of each Category in the Array.
Question:
How would I search for the slug names [ 'mens-shirts', 'clearance' ], in the array of Categories (Type: Category[]), using TypeORM's QueryBuilder? Or How could I check to see if each Category in the Categories field, has a slug, that is in [ 'mens-shirts', 'clearance' ]. Is it possible?
You need to use = ANY() when searching if an element is inside an array. So change 'category.slug IN (:...slugs)' to 'category.slug = ANY(:slugs)' (notice how you don't need to use "spread" inside ANY, just reference the array directly)

Possible to group multiple entries and form new object for the values?

I have Orders and Products being connected by a third collection with OrderID, ProductID, Quantity.
I want to get the result grouped something like this:
{
OrderID: 1,
products:[
name: productname1, quantity: 3
name: productname2, quantity: 1
]
},
{
OrderID: 2,
products:[
name: productname3, quantity: 10
name: productname5, quantity: 4
]
}
I have managed to $lookup and $project it down to displaying each coupling/reference on its own:
{
OrderID: 1,
name: productname1,
quantity: 3
},
{
OrderID: 1,
name: productname2,
quantity: 1
},
{
OrderID: 2
...
},
{
OrderID: 2
...
}
Is this to any use, or am i thinking wrong here? I basically tried $push into an object, but that doesn't really work.
Solved it after some head scratching.
Rearranged the last $project for this result instead:
{
OrderID: 1,
product: [{
name: productname1,
quantity: 3
}]
}
I could then run this $group and get my desired result (pushing each entire object from array):
{
_id: "$OrderID",
products: {$push: "$product"}
}

Sequelize: on a subset of model A, sum an integer-attribute of an associated model B

I want to do this:
select sum("quantity") as "sum"
from "orderArticles"
inner join "orders"
on "orderArticles"."orderId"="orders"."id"
and "orderArticles"."discountTagId" = 2
and "orders"."paid" is not null;
which results in on my data base:
sum
-----
151
(1 row)
How can I do it?
My Sequelize solution:
The model definitions:
const order = Conn.define('orders', {
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
primaryKey: true
},
// ...
paid: {
type: Sequelize.DATE,
defaultValue: null
},
// ...
},
// ...
})
const orderArticle = Conn.define('orderArticles',
{
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
primaryKey: true
},
// ...
quantity: {
type: Sequelize.INTEGER,
defaultValue: 1
}
},
{
scopes: {
paidOrders: {
include: [
{ model: order, where: { paid: {$ne: null}} }
]
}
},
// ...
})
Associations:
orderArticle.belongsTo(order)
order.hasMany(orderArticle, {onDelete: 'cascade', hooks: true})
I came up with this after hours of research:
db.models.orderArticles
.scope('paidOrders') // select only orders with paid: {$ne: null}
.sum('quantity', { // sum up all resulting quantities
attributes: ['quantity'], // select only the orderArticles.quantity col
where: {discountTagId: 2}, // where orderArticles.discountTagId = 2
group: ['"order"."id"', '"orderArticles"."quantity"'] // don't know why, but Sequelize told me to
})
.then(sum => sum) // return the sum
leads to this sql:
SELECT "orderArticles"."quantity", sum("quantity") AS "sum",
"order"."id" AS "order.id", "order"."taxRate" AS "order.taxRate",
"order"."shippingCosts" AS "order.shippingCosts", "order"."discount"
AS "order.discount", "order"."paid" AS "order.paid",
"order"."dispatched" AS "order.dispatched", "order"."payday" AS
"order.payday", "order"."billNr" AS "order.billNr",
"order"."createdAt" AS "order.createdAt", "order"."updatedAt" AS
"order.updatedAt", "order"."orderCustomerId" AS
"order.orderCustomerId", "order"."billCustomerId" AS
"order.billCustomerId" FROM "orderArticles" AS "orderArticles" INNER
JOIN "orders" AS "order" ON "orderArticles"."orderId" = "order"."id"
AND "order"."paid" IS NOT NULL WHERE "orderArticles"."discountTagId" =
'4' GROUP BY "order"."id", "orderArticles"."quantity";
which has this result on the same data base: 0 rows
If you know what I got wrong please let me know!
Thank you :)
Found the solution:
in the scopes definition on the orderArticle model:
scopes: {
paidOrders: {
include: [{
model: order,
where: { paid: {$ne: null}},
attributes: [] // don't select additional colums!
}]
}
},
//...
and the algorithm:
db.models.orderArticles
.scope('paidOrders')
.sum('quantity', {
attributes: [], // don't select any further cols
where: {discountTagId: 2}
})
Note: In my case it was sufficient to return the promise. I use GraphQL which resolves the result and sends it to the client.

lodash casting, mapping and picking

I have an array of objects that I am simplifying as this:
var expenses = [
{
arm: "0",
cat_id: "1",
crop: {
crop: "corn",
id: 1
},
crop_id: "1",
dist: "164.97",
expense: "Fertilizer",
id: "1",
loan_id: "1"
},
{
arm: "20",
cat_id: "8",
crop: {
crop: "corn",
id: 1
},
crop_id: "1",
dist: "0",
expense: "Labor",
id: "8",
loan_id: "1"
}
];
I am trying to end up with this:
var expenses = [{
arm: 0,
cat_id: 1,
crop: "corn",
crop_id: 1,
dist: 164.97,
expense: "Fertilizer",
id: 1,
loan_id: 1
},{
arm: 20,
cat_id: 6,
crop: "corn",
crop_id: 1,
dist: 0,
expense: "Labor",
id: 1,
loan_id: 1
}];
I can get certain pieces in that direction but can't pull it all together without error. I can't find out how to cast the values to float or put crop INSIDE of stub because casted returns all nulls. I currently have this:
flattened = _.map(expenses, function(item){
var crop = item.crop.crop;
var stub = _.pick(item, [
'id',
'loan_id',
'cat_id',
'expense',
'crop_id',
'arm',
'dist'
]);
var casted = _.map(stub, function(i){
i.crop = crop;
return i;
});
return stub;
});
Any help is appreciated.
Problem 1: I can't find out how to cast the values to float
This should be easily fixed by using parseFloat.
e.g.
item.dist = parseFloat(item.dist);
Problem 2: put crop INSIDE of stub because casted returns all nulls
Since you're already using lodash, might as well get used to their chaining feature (lazy evaluation).
DEMO
var flattened = _.map(expenses, function(item) {
item.dist = parseFloat(item.dist);
return _(item)
.omit('crop')
.assign(_.omit(item.crop, 'id'))
.value();
});
The solution above maps the entire expenses array, converting item.dist to a floating point value and then flattening the values from the item.crop object towards the item object with the exception of the item.crop.id value.
Note: In regards to your solution above, using _.map in an object
results to an array.
My attempt for my own learning purposes based on #ryeballar code.
var flattened = _.map(expenses, function(item) {
return _(item)
.set('crop', item.crop.crop) // set item.crop
.set('dist', parseFloat(item.dist)) // set dist as (float)item.dist
.value();
});

Kendo UI BarChart Data Grouping

Not sure if this is possible. In my example I am using json as the source but this could be any size. In my example on fiddle I would use this data in a shared fashion by only binding two columns ProductFamily (xAxis) and Value (yAxis) but I would like to be able to group the columns by using an aggregate.
In this example without the grouping it shows multiple columns for FamilyA. Can this be grouped into ONE column and the values aggregated regardless of the amount of data?
So the result will show one column for FamilyA of Value 4850 + 4860 = 9710 etc.?
A problem with all examples online is that there is always the correct amount of data for each category. Not sure if this makes sense?
http://jsfiddle.net/jqIndy/ZPUr4/3/
//Sample data (see fiddle for complete sample)
[{
"Client":"",
"Date":"2011-06-01",
"ProductNumber":"5K190",
"ProductName":"CABLE USB",
"ProductFamily":"FamilyC",
"Status":"OPEN",
"Units":5000,
"Value":5150.0,
"ShippedToDestination":"CHINA"
}]
var productDataSource = new kendo.data.DataSource({
data: dr,
//group: {
// field: "ProductFamily",
//},
sort: {
field: "ProductFamily",
dir: "asc"
},
//aggregate: [
// { field: "Value", aggregate: "sum" }
//],
//schema: {
// model: {
// fields: {
// ProductFamily: { type: "string" },
// Value: { type: "number" },
// }
// }
//}
})
$("#product-family-chart").kendoChart({
dataSource: productDataSource,
//autoBind: false,
title: {
text: "Product Family (past 12 months)"
},
seriesDefaults: {
overlay: {
gradient: "none"
},
markers: {
visible: false
},
majorTickSize: 0,
opacity: .8
},
series: [{
type: "column",
field: "Value"
}],
valueAxis: {
line: {
visible: false
},
labels: {
format: "${0}",
skip: 2,
step: 2,
color: "#727f8e"
}
},
categoryAxis: {
field: "ProductFamily"
},
legend: {
visible: false
},
tooltip: {
visible: true,
format: "Value: ${0:N0}"
}
});​
The Kendo UI Chart does not support binding to group aggregates. At least not yet.
My suggestion is to:
Move the aggregate definition, so it's calculated per group:
group: {
field: "ProductFamily",
aggregates: [ {
field: "Value",
aggregate: "sum"
}]
}
Extract the aggregated values in the change handler:
var view = products.view();
var families = $.map(view, function(v) {
return v.value;
});
var values = $.map(view, function(v) {
return v.aggregates.Value.sum;
});
Bind the groups and categories manually:
series: [ {
type: "column",
data: values
}],
categoryAxis: {
categories: families
}
Working demo can be found here: http://jsbin.com/ofuduy/5/edit
I hope this helps.