How to do grouping by in JS? - javascript

I have an API which returns a JSON object like this:
plans: [{
0 : {plan: "gold", amount: 5000},
1: {plan: "silver", amount: 2000},
3: {plan: "silver", amount: 1000},
4: {plan: "gold", amount: -4000}
}]
I want to group the result by plan. So here is the expected result:
groupedPlans: [{
"gold" : {amount: 1000},
"silver" : {amount: 3000}
}]
Is it possible to do such a thing using JS? I know in other languages like SQL you can do that simply by using GROUP BY plan. But not sure how can I do that in JS.

You could get a flat array and group by plan.
const
plans = [{ 0: { plan: "gold", amount: 5000 }, 1: { plan: "silver", amount: 2000 }, 3: { plan: "silver", amount: 1000 }, 4: { plan: "gold", amount: -4000 } }],
result = plans
.flatMap(Object.values)
.reduce((r, { plan, amount }) => {
(r[plan] ??= { amount: 0 }).amount += amount;
return r;
}, {});
console.log(result);

Related

Update multiple product values with one dataLayer.push

Say I have the following dataLayer:
{
ecommerce: {
currencyCode: "USD",
purchase: {
actionField: {
id: "1a6d5021",
affiliation: "Online Store",
revenue: 40,
tax: 0,
shipping: "",
coupon: ""
},
products: [
{
name: "Product 1",
id: "123",
price: 40,
category: null,
quantity: 1,
coupon: "disc10",
type: "Service A"
},
{
name: "Product 4",
id: "456",
price: 40,
category: null,
quantity: 1,
coupon: "disc10",
type: "Service B"
}
]
}
}
}
So in the product array, category always has value null. How can I push the same value as type respectively for each product, whilst leaving everything else in the dataLayer untouched?
Ultimately the final result that I am trying to achieve would be like this:
{
ecommerce: {
currencyCode: "USD",
purchase: {
actionField: {
id: "1a6d5021",
affiliation: "Online Store",
revenue: 40,
tax: 0,
shipping: "",
coupon: ""
},
products: [
{
name: "Product 1",
id: "123",
price: 40,
category: "Service A",
quantity: 1,
coupon: "disc10",
type: "Service A"
},
{
name: "Product 4",
id: "456",
price: 40,
category: "Service B",
quantity: 1,
coupon: "disc10",
type: "Service B"
}
]
}
}
}
It be easy with a single product, but I quite can't find how to do it when multiple products.
Thanks in advance for your help.
If I understand your requirement correctly that you want to assign the type value in the category for each products object. If Yes, Its a straight forward.
Working Demo :
const productObj = {
ecommerce: {
currencyCode: "USD",
purchase: {
actionField: {
id: "1a6d5021",
affiliation: "Online Store",
revenue: 40,
tax: 0,
shipping: "",
coupon: ""
},
products: [{
name: "Product 1",
id: "123",
price: 40,
category: null,
quantity: 1,
coupon: "disc10",
type: "Service A"
},
{
name: "Product 4",
id: "456",
price: 40,
category: null,
quantity: 1,
coupon: "disc10",
type: "Service B"
}
]
}
}
};
productObj.ecommerce.purchase.products.forEach((obj) => obj.category = obj.type);
console.log(productObj);
There are two options.
Push the whole ecommerce object again, with all fields set now. It results in a bit of a mess in DL and certain timing issues one has to keep in mind when implementing tracking.
Remove/delay the first ecommerce push till you have all info and only push the ecommerce object once.
In most cases, 2 is the best option. 1 can be justified when the event relying on the ecommerce object has to fire before categories become available to the front-end.
Try
function createCategoryFn(category) {
return (properties) => {
return {
name: "",
id: "",
price: 0,
category: category,
quantity: 1,
coupon: "",
type: category,
...properties
};
};
}
const createSportsProduct = createCategoryFn('Sports');
const tennisProduct = createSportsProduct({ name: 'tennis racket', id: 1, price: 100 });
const basketballProduct = createSportsProduct({ name: 'basketball', id: 2, price: 100 });
console.log(tennisProduct);
console.log(basketballProduct)

Javascript Filter an Array to give subset from two Range Parameters

How do I filter an array two give me the subset created by overlap of two ranges?
For example, suppose Here's my array with a bunch of objects:
const list = [{
id: 1,
price: "10",
weight: "19.45"
},{
id: 2,
price: "14",
weight: "27.8"
},{
id: 3,
price: "45",
weight: "65.7"
},{
id: 4,
price: "37",
weight: "120.5"
},{
id: 5,
price: "65",
weight: "26.9"
},{
id: 6,
price: "120",
weight: "19.3"
},{
id: 7,
price: "20",
weight: "45.7"
}]
Now I want to filter the above array of objects based on a range for two parameters price and weight.
let range = {
startPrice: 15,
endPrice: 60,
startWeight: 22.0,
endWeight: 70.5,
}
I want to filter my list with these range parameters which will return me an array with a subset of objects satisfying both the filter ranges. Hence, the output array should be:
filtered = [{
id: 3,
price: "45",
weight: "65.7"
},{
id: 7,
price: "20",
weight: "45.7"
}]
Because items with id: 3 and id: 5 satisfy the subset of both of the two ranges. How should my list.filter() function look like? Any help is greatly appreciated, thank you.
Note: I've used parseFloat because values are stored as a String. There are other ways to get a Number out of a String.
var list = [{"id":1,"price":"10","weight":"19.45"},{"id":2,"price":"14","weight":"27.8"},{"id":3,"price":"45","weight":"65.7"},{"id":4,"price":"37","weight":"120.5"},{"id":5,"price":"65","weight":"26.9"},{"id":6,"price":"120","weight":"19.3"},{"id":7,"price":"20","weight":"45.7"}];
var range = {
startPrice: 15,
endPrice: 60,
startWeight: 22.0,
endWeight: 70.5
};
var filtered=list.filter(
element=> {
var elementprice=parseFloat(element.price);
var elementweight=parseFloat(element.weight);
if(
elementprice>=range.startPrice &&
elementprice<=range.endPrice &&
elementweight>=range.startWeight &&
elementweight<=range.endWeight
) {
return true;
}
return false;
}
);
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You mean 3 and 7
Also you need to either remove the quotes or cast to number. Note a filter will expect a true or false to a Boolean test is enough. No if or else needed
const list = [{ id: 1, price: "10", weight: "19.45" }, { id: 2, price: "14", weight: "27.8" }, { id: 3, price: "45", weight: "65.7" }, { id: 4, price: "37", weight: "120.5" }, { id: 5, price: "65", weight: "26.9" }, { id: 6, price: "120", weight: "19.3" }, { id: 7, price: "20", weight: "45.7" }];
let range = { startPrice: 15, endPrice: 60, startWeight: 22.0, endWeight: 70.5, }
const res = list.filter(item =>
+item.price >= range.startPrice &&
+item.price <= range.endPrice &&
+item.weight >= range.startWeight &&
+item.weight <= range.endWeight);
console.log(res);

How to sum multiple different objects in an array using Vuejs?

I am getting started with Vue. I am struggling to calculate the sum of different elements in an object of an array.
My array looks like this:
sites: [{
sku: 10001,
name: "Product A",
totalPrice: '',
values: [{
price: 10,
discount: 5,
color: "red"
},
{
price: 15,
discount: 8,
color: "black"
}]
},
{
sku: 10002,
name: "Product B",
totalPrice: '',
values: [{
price: 13,
discount: 3,
color: "purple"
},
{
price: 20,
discount: 5,
color: "green"
}]
}]
I am trying to sum the price and set it to totalPrice. So the array will change totalPrice as below:
sku: 10001,
name: "Product A",
totalPrice: 25,
sku: 10002,
name: "Product B",
totalPrice: 33,
I believe I need to use something like the below to sum them, however I cannot figure out how to do this!
computed: {
total(){ return this.sites.reduce( (total, item) => item.values. price + total ,0);}
},
How do I calculate the sum of the price and set it as the totalPrice?
I have traveled SO and find similar threads however nothing that I can get to work with my issue.
computed: {
total() {
let newojv = []
sites.forEach((item, _) => {
let s = item.values.map((items2, _) => {
return items2.price;
})
let sum = s.reduce((a, b) => a + b);
newojv.push({
sku: item.sku,
name: item.name,
totalPrice: sum
});
});
return newojv;
}
}
First for Each of the array of objects below
{
sku: 10001,
name: "Product A",
totalPrice: '',
values: [{
price: 10,
discount: 5,
color: "red"
},
{
price: 15,
discount: 8,
color: "black"
}
]
}
And then for Each of the array of objects below
values: [{
price: 10,
discount: 5,
color: "red"
},
{
price: 15,
discount: 8,
color: "black"
}
]
We take a we map the array to get the values of the price, which is 10,15. Then we reduce the array, add it and then push it.
let sum = s.reduce((a, b) => a + b);
newojv.push({
sku: item.sku,
name: item.name,
totalPrice: sum
});
A working example can be
let sites = [{
sku: 10001,
name: "Product A",
totalPrice: '',
values: [{
price: 10,
discount: 5,
color: "red"
},
{
price: 15,
discount: 8,
color: "black"
}
]
}, {
sku: 10002,
name: "Product B",
totalPrice: '',
values: [{
price: 13,
discount: 3,
color: "purple"
},
{
price: 20,
discount: 5,
color: "green"
}
]
}]
let newojv = []
sites.forEach((item, _) => {
let s = item.values.map((items2, _) => {
return items2.price;
})
let sum = s.reduce((a, b) => a + b);
newojv.push({
sku: item.sku,
name: item.name,
totalPrice: sum
});
});
console.log(newojv)

How to sum key value of all documents

I am just new to Mongo DB an facing problem in summation of the key value of all documents...
Here I am providing the sample data of Mongo DB-
{ _id: 1, cust_id: "abc1", ord_date: ISODate("2012-11-02T17:04:11.102Z"), status: "A", amount: 50 }
{ _id: 2, cust_id: "xyz1", ord_date: ISODate("2013-10-01T17:04:11.102Z"), status: "A", amount: 100 }
{ _id: 3, cust_id: "xyz1", ord_date: ISODate("2013-10-12T17:04:11.102Z"), status: "D", amount: 25 }
{ _id: 4, cust_id: "xyz1", ord_date: ISODate("2013-10-11T17:04:11.102Z"), status: "D", amount: 125 }
{ _id: 5, cust_id: "abc1", ord_date: ISODate("2013-11-12T17:04:11.102Z"), status: "A", amount: 25 }
All i want is to get the total value of key "amount", like for example-
50+100+25+125+25
You can query like this:
db.yourCollection.aggregate({ $group: { _id : null, sum : { $sum: "$amount" } } });
More details about aggregation can be found at here, and more details about sum can be found here

What's the most efficent way to populate an a property of an array of objects with a larger array with data?

I have a small array of objects with properties, like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [ <PLACE VALUES HERE> ],
},
{
topicName: 'Cost',
topic: 2,
dates: [ <PLACE VALUES HERE> ],
},
];
Then I have a large array of objects that I wish to extract some of the properties from in to the above dates array.
Here's what the data I wish to extract from:
[
{
"date": "2014-02-01",
"device": "Computer",
"match-type": "NA",
"clicks": 66,
"revenue": 1037,
"conversions": 2,
"cost": 284.35,
"impressions": 5330,
"ROI": 3.64691401441885
},
{
"date": "2014-02-01",
"device": "Tablet",
"match-type": "NA",
"clicks": 38,
"revenue": 587,
"conversions": 2,
"cost": 194.01000000000005,
"impressions": 1934,
"ROI": 3.025617236224936
},
{
"date": "2014-02-02",
"device": "Tablet",
"match-type": "NA",
"clicks": 40,
"revenue": 587,
"conversions": 2,
"cost": 190,
"impressions": 1934,
"ROI": 3.025617236224936
},
]
Now I need the data from all of the members of the last array and insert that releveant data for the particular object in the first array (totalling where necessary), like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [
{
date: '2014-02-01',
value: 104
},
{
date: '2014-02-02',
value: 40
}
],
},
{
topicName: 'Cost',
topic: 2,
dates: [
{
date: '2014-02-01',
value: 284,3519401
},
{
date: '2014-02-02',
value: 190
}
],
},
];
The target is the latest version of Chrome and I'm using Webpack with Babel so all the latest stuff is available.
Assuming the last dataset can be pretty large, what's the most efficient way to go about this?
[EDIT]
This is what I've come up with so far:
const dataAdapter = rawData => {
const topics = ['clicks', 'revenue', 'cost', 'roi'];
const topicsData = topics.map((topic, index) => {
const thisTopic = {};
thisTopic.topicName = topic;
thisTopic.topic = index;
thisTopic.dates = [];
return thisTopic;
});
const convertedData = topicsData.map(topicData => {
const thisTopic = topicData;
const map = new Map();
rawData.forEach(elem => {
map.set(elem.date, (map.get(elem.date) || 0) + elem[[thisTopic.topicName]]);
});
thisTopic.dates = Array.from(map);
return thisTopic;
});
return convertedData;
};
Thanks,
/J
You could take an object as reference to the wanted keys and date. Then iterate data and check if a reference to a result set exists. If not, create a new result set.
var result = [{ topicName: 'Clicks', topic: 1, dates: [], }, { topicName: 'Cost', topic: 2, dates: [], }],
data = [{ date: "2014-02-01", device: "Computer", "match-type": "NA", clicks: 66, revenue: 1037, conversions: 2, cost: 284.35, impressions: 5330, ROI: 3.64691401441885 }, { date: "2014-02-01", device: "Tablet", "match-type": "NA", clicks: 38, revenue: 587, conversions: 2, cost: 194.01000000000005, impressions: 1934, ROI: 3.025617236224936 }, { date: "2014-02-02", device: "Tablet", "match-type": "NA", clicks: 40, revenue: 587, conversions: 2, cost: 190, impressions: 1934, ROI: 3.025617236224936 }],
hash = { clicks: { _: result[0].dates }, cost: { _: result[1].dates }, };
data.forEach(function (o) {
['clicks', 'cost'].forEach(function (k) {
if (!hash[k][o.date]) {
hash[k][o.date] = { date: o.date, value: o[k] };
hash[k]._.push(hash[k][o.date]);
return;
}
hash[k][o.date].value += o[k];
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming your data is in data variable and topics are in topics variable. This solution uses only javascript builtin objects.
const getPropDateMap = (obj, prop) => obj
.reduce((accObj, item) => {
return Object.assign(accObj, {
[item.date]: item.clicks + (accObj[item.date] || 0)
})
}, {})
topics.forEach(topic => {
topic.dates = Object
.entries(getPropDateMap(data, topic.topicName.toLowerCase()))
.map(entry => ({date: entry[0], value: entry[1]}))
})

Categories