Group data based on property javascript - javascript

I have json array as follows,
{
"lstMonths": [
"September"
],
"lstBalances": [
{
"AccountName": "john",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "-28.11"
}
]
},
{
"AccountName": "sanga\n",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "100"
}
]
},
{
"AccountName": "mahela",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "400"
}
]
},
{
"AccountName": "sachin",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "600"
}
]
},
{
"AccountName": "dhoni",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "-500"
}
]
}
]
}
i want to group by AccountName and form the output as,
{ data: [28.11], label: 'john' },
{ data: [100], label: 'sanga' },
{ data: [400], label: 'mahela' },
{ data: [400], label: 'sachin' }
{ data: [400], label: 'dhoni' }
if there are two months, say october in above then
{ data: [28.11,3], label: 'john' },
{ data: [100,4], label: 'sanga' },
{ data: [400,4], label: 'mahela' },
{ data: [400,4], label: 'sachin' }
{ data: [400,5], label: 'dhoni' }
i tried to group like as above. any help guys?
here is my code:
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
console.log(groupBy(response.lstMonths, 'length'));
please provide a way to find the best value.
EDIT
json for more months,
{ "lstMonths": [
"September" ], "lstBalances": [
{
"AccountName": "john\n",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "-28.11"
},
{
"MonthName": "August",
"Amount": "-28.11"
}
]
},
{
"AccountName": "sanga\n",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "100"
},
{
"MonthName": "August",
"Amount": "233"
}
]
},
{
"AccountName": "mahela",
"AccountBalanceOnMonth": [
{
"MonthName": "September",
"Amount": "400"
},
{
"MonthName": "August",
"Amount": "32"
}
]
} ] }

Assuming,
only one array with data,
for every month is a value,
all months are sorted
result months should have the same sorting,
then you could take the array with accounts and iterate the accounts for building a new object for each account and take the balance values for the data array.
var data = [{ lstMonths: ["September"], lstBalances: [{ AccountName: "john", AccountBalanceOnMonth: [{ MonthName: "September", Amount: "-28.11" }, { MonthName: "August", Amount: "-28.11" }] }, { AccountName: "sanga", AccountBalanceOnMonth: [{ MonthName: "September", Amount: "100" }, { MonthName: "August", Amount: "233" }] }, { AccountName: "mahela", AccountBalanceOnMonth: [{ MonthName: "September", Amount: "400" }, { MonthName: "August", Amount: "32" }] }] }],
grouped = data[0].lstBalances.map(function (o) {
return {
data: o.AccountBalanceOnMonth.map(function (p) { return +p.Amount; }),
label: o.AccountName
};
});
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to group yield value based on date key from array of object in javascript

I have an array of objects
var data = [
{
"date": "2016-07-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": 'Wheat',
"yield": 27,
},
{
"crop": 'Maize',
"yield": 48,
}
]
},
{
"date": "2020-03-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": "garlic",
"yield": 12,
},
{
"crop": "Wheat",
"yield": 12,
}
]
},
{
"date": "2020-07-18T00:00:00.000+0000",
"boundaryStats": [{
"crop": "Onion",
"yield": 1,
}
]
}
];
How can I get the output as below only unique crop name will be added and the length of data is based on length of the input array date, 0 if there is no data for the particular date
[{
"label": "Wheat",
"data": [27,12,0]
},
{
"label": "Maize",
"data": [48,0,0]
},
{
"label": "garlic",
"data": [0,12,0]
},
{
"label": "Onion",
"data": [0,0,1]
}
]
I tried
main = [];
resultObj = new Array(this.data.length).fill(0);
this.data.forEach(e=>{
e.boundaryStats.forEach((ei,i)=>{
const checkUsername = obj => obj.label === ei.crop;
if(!main.some(checkUsername)){
main.push({
'label':ei.crop,
'data':resultObj
})
}
})
})
but yield I am not able to add a particular data index
You could group with an object and get the values.
var data = [{ date: "2016-07-18T00:00:00.000+0000", boundaryStats: [{ crop: "Wheat", yield: 27 }, { crop: "Maize", yield: 48 }] }, { date: "2020-03-18T00:00:00.000+0000", boundaryStats: [{ crop: "garlic", yield: 12 }, { crop: "Wheat", yield: 12 }] }, { date: "2020-07-18T00:00:00.000+0000", boundaryStats: [{ crop: "Onion", yield: 1 }] }],
result = Object.values(data.reduce((r, { date, boundaryStats }, i, { length }) => {
boundaryStats.forEach(({ crop: label, yield: value }) => {
if (!r[label]) r[label] = { label, data: Array(length).fill(0) };
r[label].data[i] = value;
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Uses an object map to collect values. map to desired {label,data} objects at the end.
const map = {}
data.forEach(({boundaryStats: b},i)=>b.forEach(({crop, yield})=>{
map[crop] = map[crop] || Array(data.length).fill(0)
map[crop][i] = yield
}))
console.log(
Object.entries(map).map(([crop,yield])=>({label:crop, data:yield}))
)
<script>
var data = [
{
"date": "2016-07-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": 'Wheat',
"yield": 27,
},
{
"crop": 'Maize',
"yield": 48,
}
]
},
{
"date": "2020-03-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": "garlic",
"yield": 12,
},
{
"crop": "Wheat",
"yield": 12,
}
]
},
{
"date": "2020-07-18T00:00:00.000+0000",
"boundaryStats": [{
"crop": "Onion",
"yield": 1,
}
]
}
];
</script>
var data = [
{
"date": "2016-07-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": 'Wheat',
"yield": 27,
},
{
"crop": 'Maize',
"yield": 48,
}
]
},
{
"date": "2020-03-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": "garlic",
"yield": 12,
},
{
"crop": "Wheat",
"yield": 12,
}
]
},
{
"date": "2020-07-18T00:00:00.000+0000",
"boundaryStats": [{
"crop": "Onion",
"yield": 1,
}
]
}
];
main = [];
data.forEach(e=>{
e.boundaryStats.forEach((ei,i)=>{
const checkUsername = obj => obj.label === ei.crop;
if(!main.some(checkUsername)){
main.push({
'label':ei.crop,
'data': [ei.yield]
})
} else {
let obj = main.find(m => m.label === ei.crop);
obj.data.push(ei.yield);
}
})
})
console.log(main)
We can do it with reduce:
const dateArr = data.map(item => (new Date(item.date)).toLocaleString()); //can be sorted if needed
console.log(dateArr);
const tempOut = data.reduce((aggObj, item) => {
item.boundaryStats.forEach(stat => {
const statDate = (new Date(item.date)).toLocaleString();
if (!aggObj[stat.crop]){
aggObj[stat.crop] = {label: stat.crop, data: {}};
for (let date of dateArr){
aggObj[stat.crop].data[date] = 0;
}
}
aggObj[stat.crop].data[statDate] += stat.yield;
})
return aggObj;
}, {})
//console.log(tempOut)
const finalOut = Object.values(tempOut).map(crop => {
crop.data = Object.values(crop.data);
return crop
});
console.log(finalOut);
<script id="initData">
const data = [
{
"date": "2016-07-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": 'Wheat',
"yield": 27,
},
{
"crop": 'Maize',
"yield": 48,
}
]
},
{
"date": "2020-03-18T00:00:00.000+0000",
"boundaryStats": [
{
"crop": "garlic",
"yield": 12,
},
{
"crop": "Wheat",
"yield": 12,
}
]
},
{
"date": "2020-07-18T00:00:00.000+0000",
"boundaryStats": [{
"crop": "Onion",
"yield": 1,
}
]
}
];
</script>
You can first maintain a date Object with its initial count, then you can reduce it:
var data = [ { "date": "2016-07-18T00:00:00.000+0000", "boundaryStats": [ { "crop": 'Wheat', "yield": 27, }, { "crop": 'Maize', "yield": 48, } ] }, { "date": "2020-03-18T00:00:00.000+0000", "boundaryStats": [ { "crop": "garlic", "yield": 12, }, { "crop": "Wheat", "yield": 12, } ] }, { "date": "2020-07-18T00:00:00.000+0000", "boundaryStats": [{ "crop": "Onion", "yield": 1, } ] }];
var datesOrder = Object.fromEntries(data.map(({date})=>([new Date(date).toLocaleDateString(),0])));
result = Object.values(data.reduce((acc, {date, boundaryStats})=>{
boundaryStats.forEach(k=>{
acc[k.crop] = acc[k.crop] || {label:k.crop, data:{...datesOrder}};
acc[k.crop].data[new Date(date).toLocaleDateString()] = k.yield;
});
return acc;
},{}))
.map(({label, data})=>({label, data:Object.values(data)}));
console.log(result);

TypeError: Cannot read property 'reduce' of undefined at getTotal() in javascript [duplicate]

This question already has answers here:
How to return and get the sum of object properties based on given filters?
(2 answers)
Closed 3 years ago.
From the below code, I want to get the value of Total based on my input values/filters. If I use .filter() method it is not working in my application(getting empty response or 0), but working in externally standalone page, I am not sure why. Please help me. Thanks.
With the below code, I am getting the error in my application: TypeError: Cannot read property 'reduce' of undefined at getTotal()
index.js:
function getTotal(data, filters) {
console.log(JSON.stringify(filters));//{"Name":"ABC","Dept":"First","FY":"2016","Quarter":"1","Month":"April"}
console.log(JSON.stringify(data));//[{"Name":"ABC","Dept":"First","FY":"2016","Quarter":"1","Month":"April","Total":"1200"},{"Name":"ABC","Dept":"Second","FY":"2017","Quarter":"2","Month":"May","Total":"200"},{"Name":"ABC","Dept":"First","FY":"2016","Quarter":"1","Month":"June","Total":"150"},{"Name":"DEF","Dept":"First","FY":"2016","Quarter":"1","Month":"April","Total":"200"},{"Name":"DEF","Dept":"Second","FY":"2017","Quarter":"2","Month":"May","Total":"100"},{"Name":"DEF","Dept":"First","FY":"2016","Quarter":"1","Month":"June","Total":"500"}]
var f = Object.entries(filters);
console.log("f is: "+f);// Name,ABC,Dept,First,FY,2016,Quarter,1,Month,April
const test = data.find(o => f.every(([k, v]) => o[k] === v)).reduce((s, { Total }) => s + +Total, 0);
return test;//Output should be: 100
}
var data = [{ Name: "ABC", Dept: "First", FY: "2016", Quarter: "1", Month: "April", Total: "100" }, { Name: "ABC", Dept: "Second", FY: "2017", Quarter: "2", Month: "May", Total: "200" }, { Name: "ABC", Dept: "First", FY: "2016", Quarter: "1", Month: "June", Total: "150" }, { Name: "DEF", Dept: "First", FY: "2016", Quarter: "1", Month: "April", Total: "200" }, { Name: "DEF", Dept: "Second", FY: "2017", Quarter: "2", Month: "May", Total: "100" }, { Name: "DEF", Dept: "First", FY: "2016", Quarter: "1", Month: "June", Total: "500" }];
console.log(getTotal(data, { Name: 'ABC', Dept: 'First', FY: '2016', Quarter: '1', Month: 'April' })); // shoud give: 100
Yes, got the answer now at: How to return and get the sum of object properties based on given filters?
You should use Array.prototype.filter instead of Array.prototype.find as it returns an array instead of a single object from your array.
Objects don't have a reduce method arrays do:
function getTotal(data, filters) {
var f = Object.entries(filters);
const test = data.filter(o => f.every(([k, v]) => o[k] === v)).reduce((s, { Total }) => s + +Total, 0);
return test;//Output should be: 100
}
var data = [{ Name: "ABC", Dept: "First", FY: "2016", Quarter: "1", Month: "April", Total: "100" }, { Name: "ABC", Dept: "Second", FY: "2017", Quarter: "2", Month: "May", Total: "200" }, { Name: "ABC", Dept: "First", FY: "2016", Quarter: "1", Month: "June", Total: "150" }, { Name: "DEF", Dept: "First", FY: "2016", Quarter: "1", Month: "April", Total: "200" }, { Name: "DEF", Dept: "Second", FY: "2017", Quarter: "2", Month: "May", Total: "100" }, { Name: "DEF", Dept: "First", FY: "2016", Quarter: "1", Month: "June", Total: "500" }];
console.log(getTotal(data, { Name: 'ABC', Dept: 'First', FY: '2016', Quarter: '1', Month: 'April' })); // shoud give: 100

How to Show Same Month name of two years in X axis of Highcharts

I have a Highcharts chart in my application. I need to display how many cars have been sold in each month of the year and this chart shows car selling over 13 months time period(Ex: June 2017- June 2018) so that same month(of two years) is displayed two times. X category should be something like (Jun, Jul, Aug, Sep, Oct, Nov, Dec, Jan, Feb, Mar, Apr, May, Jun)
But the problem is chart shows only one "Jun" month in the graph. I think category name duplication is the error. Is there a workaround for this to get done?
Here is what I have tried so far:
// Create the chart
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Car Sales'
},
xAxis: {
type: 'category'
},
yAxis: {
title: {
text: 'Total percent car salee'
}
},
legend: {
enabled: false
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
format: '{point.y:.1f}%'
}
}
},
tooltip: {
headerFormat: '<span style="font-size:11px">{series.name}</span><br>',
pointFormat: '<span style="color:{point.color}">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>'
},
"series": [
{
"name": "Browsers",
"colorByPoint": true,
"data": [
{
"name": "January",
"y": 62.74,
},
{
"name": "February",
"y": 10.57,
},
{
"name": "March",
"y": 7.23,
},
{
"name": "April",
"y": 5.58,
},
{
"name": "May",
"y": 4.02,
},
{
"name": "June",
"y": 1.92,
},
{
"name": "July",
"y": 7.62,
},
{
"name": "August",
"y": 7.62,
},
{
"name": "September",
"y": 7.62,
},
{
"name": "October",
"y": 7.62,
},
{
"name": "November",
"y": 7.62,
},
{
"name": "December",
"y": 7.62,
},
{
"name": "January",
"y": 7.62,
}
]
}
],
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/data.js"></script>
<script src="https://code.highcharts.com/modules/drilldown.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
JSfiddle link: https://jsfiddle.net/yasirunilan/2ryfnv3q/
The easiest way to get to achieve this inside the drilldown, is to 'cheat' with the name of the category. If you are able to add in a space (or any other random character) at the end of the second Jan in the drilldown, it will solve the problem.
Click here for a fiddle demonstrating the solution.
"drilldown": {
"series": [
{
"name": "Chrome",
"id": "Chrome",
"data": [
{
"name": "Jan",
"y": 0.1
},
{
"name": "Feb",
"y": 0.1
},
//snip
{
"name": "Jan ",
"y": 0.1
}
]
},

How to extract parts of nested multi-level JSON objects / array and flatten it with JavaScript

I'm working with the following data structure:
"data": {
"products": [
[
{
"category": "A",
"items": [
{
"name": "Aloe",
"price": 10
},
{
"name": "Apples",
"price": 5
}
]
},
{
"category": "B",
"items": [
{
"name": "Bread",
"price": 5
}
]
}
],
[
{
"category": "C",
"items": [
{
"name": "Candy",
"price": 5
},
{
"name": "Crayon",
"price": 5
}
]
},
{
"category": "D",
"items": [
{
"name": "Dice",
"price": 5
},
{
"name": "Doll",
"price": 10
}
]
}
]
]
}
I'd like extract parts of it to flatten so the results is as follows:
[
{
"name": "Aloe",
"price": 10
},
{
"name": "Apples",
"price": 5
},
{
"name": "Bread",
"price": 5
},
{
"name": "Candy",
"price": 5
},
{
"name": "Crayon",
"price": 5
},
{
"name": "Dice",
"price": 5
},
{
"name": "Doll",
"price": 10
}
]
How can I accomplish this?
I have tried this:
for (var sets in data.products) {
for (var categories in sets) {
for (var items in categories) {
for (var item in items) {
// assemble new array
}
}
}
}
... but had problems looping through the children objects. I've found a couple other similar questions, but they seem to address simpler data structures, and flatten the entire object rather than parts of it.
Any pointers would be appreciated.
input.data.products. ....
maube Change in row 1 depend on your variable name
var result =[]
input.data.products.forEach(function (product){
product.forEach(function(productinfo){
productinfo.items.forEach(function (item){
result.push(item)
})
})
})
You will have to try something like this:
Logic:
Loop over data.products to get individual product details.
Sine details are also arrays, loop over them and return items from them.
The trick is, you will have to use Array.prototype.concat to merge arrays returned, to flatten them.
var data = {
"products": [
[{
"category": "A",
"items": [{
"name": "Aloe",
"price": 10
},
{
"name": "Apples",
"price": 5
}
]
},
{
"category": "B",
"items": [{
"name": "Bread",
"price": 5
}]
}
],
[{
"category": "C",
"items": [{
"name": "Candy",
"price": 5
},
{
"name": "Crayon",
"price": 5
}
]
},
{
"category": "D",
"items": [{
"name": "Dice",
"price": 5
},
{
"name": "Doll",
"price": 10
}
]
}
]
]
}
var result = [].concat.apply([], data.products.map(function(product){
return [].concat.apply([], product.map(function(p){
return p.items
}))
}))
console.log(result)
I have assigned your JSON object for data to a variable first. See the code below:
var pro={
"products": [
[
{
"category": "A",
"items": [
{
"name": "Aloe",
"price": 10
},
{
"name": "Apples",
"price": 5
}
]
},
{
"category": "B",
"items": [
{
"name": "Bread",
"price": 5
}
]
}
],
[
{
"category": "C",
"items": [
{
"name": "Candy",
"price": 5
},
{
"name": "Crayon",
"price": 5
}
]
},
{
"category": "D",
"items": [
{
"name": "Dice",
"price": 5
},
{
"name": "Doll",
"price": 10
}
]
}
]
]
}
var flatArray=[];
for(var i=0; i<pro.products.length; i++){
for(var j=0; j<pro.products[i].length;j++){
for(var k=0; k<pro.products[i][j].items.length;k++){
flatArray.push(pro.products[i][j].items[k]);
}
}
}
The flatArray will be your required flattened array of objects. Hope this is clear enough.
You can use array#reduce, array#forEach and array#concat.
var input = { data: { products: [[{ category: "A", items: [{ name: "Aloe", price: 10 }, { name: "Apples", price: 5 }] }, { category: "B", items: [{ name: "Bread", price: 5 }] }], [{ category: "C", items: [{ name: "Candy", price: 5 }, { name: "Crayon", price: 5 }] }, { category: "D", items: [{ name: "Dice", price: 5 }, { name: "Doll", price: 10 }] }]] } };
const result = input.data.products.reduce((res, arr) => {
arr.forEach( o => res = res.concat(o.items));
return res;
},[]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use a recursive approach by checking the items property or return the result of reduced properties. This approach works for any depth with an expected items property at the end.
function flat(object) {
return object.items || Object.keys(object).reduce(function (r, k) {
return r.concat(flat(object[k]));
}, []);
}
var object = { data: { products: [[{ category: "A", items: [{ name: "Aloe", price: 10 }, { name: "Apples", price: 5 }] }, { category: "B", items: [{ name: "Bread", price: 5 }] }], [{ category: "C", items: [{ name: "Candy", price: 5 }, { name: "Crayon", price: 5 }] }, { category: "D", items: [{ name: "Dice", price: 5 }, { name: "Doll", price: 10 }] }]] } }
console.log(flat(object));
.as-console-wrapper { max-height: 100% !important; top: 0; }

AmCharts - Help creating a trending bar

I'm trying to follow the docs on how to add a trending line on my serial type bar graph.
AmCharts.makeChart('chartdiv', {
type: 'serial',
addClassNames: true,
theme: 'light',
dataProvider: data,
startDuration: 1,
categoryField: 'month',
graphs: [
{
valueField: 'complaints',
type: 'column',
fillAlphas: 0.8,
balloonText: "# of complaints on [[category]]: <b>[[value]]</b>"
},
{
valueField: 'expectation',
type: 'line',
// bullet: 'round',
lineColor: 'green',
balloonText: "Expected less than <b>[[value]]</b> for [[category]]",
dashLengthField: "dashLengthLine"
}
],
categoryAxis: {
autoGridCount: false,
gridCount: data.length,
gridPosition: "start",
// labelRotation: 90
},
export: {
enabled: true
},
trendLines: [{
initialValue: 6,
finalValue: 8
}]
});
The trendingLines is not doing much there. I've tried in many ways to declare it, but no luck. Here's some of the data I'm working with:
[{
"expectation": 2,
"tendValue": 1,
"month": "January",
"complaints": 1
}, {
"expectation": 2,
"month": "February",
"complaints": 2
}, {
"expectation": 2,
"month": "March",
"complaints": 0
}, {
"expectation": 2,
"month": "April",
"complaints": 1
}, {
"expectation": 2,
"month": "May",
"complaints": 0
}, {
"expectation": 2,
"month": "June",
"complaints": 1
}, {
"expectation": 2,
"month": "July",
"complaints": 2
}, {
"expectation": 2,
"month": "August ",
"complaints": 1
}, {
"expectation": 2,
"month": "September",
"complaints": 3
}, {
"expectation": 2,
"month": "October",
"complaints": 1
}, {
"expectation": 2,
"month": "November",
"complaints": 2
}, {
"expectation": 2,
"tendValue": 3,
"month": "December",
"complaints": 3
} ]
What you're missing out is the declaration of a start and an end point. You only tell the trendline its values.
Minimum code for a trendline should be sth. like this:
{
"finalDate": "2012-01-22 12",
"finalValue": 10,
"initialDate": "2012-01-17 12",
"initialValue": 16
}
or in your case using initialCategory and finalCategory. Take a look at the class reference for more info on trendline parameters or look at this nice little demo.

Categories