Changing JSON Structure in Javascript/Jquery - javascript

I have an object coming from the server via JSON which is in the below format. I want to rearrange it to another format.
{
visits: [{
week: 1,
count: 3
}, {
week: 2,
count: 3
}, {
week: 3,
count: 4
}, {
week: 4,
count: 3
}, {
week: 5,
count: 1
}],
visitors: [{
week: 1,
count: 3
}, {
week: 2,
count: 3
}, {
week: 3,
count: 4
}, {
week: 4,
count: 3
}, {
week: 5,
count: 1
}]
}
I want to change its structure and want this format below:
{
week1: {
visits: 3
visitors: 1
}
week2: {
visits: 3
visitors: 3
}
week3: {
visits: 4
visitors: 4
}
week4: {
visits: 3
visitors: 3
}
week5: {
visits: 4
visitors: 4
}
}

try this:
var newObj = {}
$.each(a.visits, function(key,obj){ newObj['week'+obj.week] ={visits: obj.count} });
$.each(a.visitors, function(key,obj){ newObj['week'+obj.week].visitors = obj.count});

so use groupBy feature :
var groupBy=function(jsonArray,key) {
return jsonArray.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
Then :
var groupedByWeeks=groupBy(yourJsonArray.visits,'week')
then map the object 1->week1, .. n->weekn
Object.keys(groupedByWeeks).map((k)=>
groupedByWeeks['week'+k]=groupedByWeeks[k];
)

Related

Find missing months in js array

I have the following Array
[
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
]
where I am missing a few months. I know how many months is needed (could be 12 or could be more or less) and I need the missing months (like 2021-08 in this case) to be added with a count of 0. How to go about it?
Here's a pure, functional approach which will create a new array with new items, inserting all of the missing months in order. The code includes some comments explaining the procedure:
const parseDate = str => str.split('-').map(Number);
const formatDate = (year, month) => `${year}-${String(month).padStart(2, '0')}`;
function createContinuousMonthCounts (array) {
const all = [];
// get initial year/month values from first item
let [year, month] = parseDate(array[0].Month);
const advanceDate = () => {
month += 1;
if (month > 12) {
year += 1;
month = 1;
}
};
for (const item of array) {
const [y, m] = parseDate(item.Month);
// while the current month is not equal to the current item's month,
// create an entry for the month, append it, and advance to the next month
while (year !== y || month !== m) {
all.push({Month: formatDate(year, month), Count: 0});
advanceDate();
}
// after we're up to date, add the current item and advance the date
all.push({...item});
advanceDate();
}
return all;
}
const array = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 },
];
const all = createContinuousMonthCounts(array);
for (const {Month, Count} of all) console.log(Month, Count);
Just a shot into the dark (please consider adding some Code to your question):
const months = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
];
const neededMonths = [
"2021-01","2021-02","2021-03","2021-04","2021-05","2021-06","2021-07","2021-08","2021-09","2021-10","2021-11","2021-12"
]
const missedMonths = [];
months.map( m => {
if(neededMonths.indexOf(m.Month) == -1 ){
missedMonths.push(m.Month);
}
});
console.log(missedMonths);
You first need a method to find all the months between a range, then iterate across all the months and add the missing ones with count: 0:
const months = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
]
const firstMonth = months.at(0).Month;
const lastMonth = months.at(-1).Month;
const [initialYear, initialMonth] = firstMonth.split('-');
const [endingYear, endingMonth] = lastMonth.split('-');
const allMonths = [];
let currentMonth = initialMonth;
let currentYear = initialYear;
while (`${currentYear}-${(''+currentMonth).padStart(2, '0')}` !== lastMonth) {
allMonths.push(`${currentYear}-${(''+currentMonth).padStart(2, '0')}`);
currentMonth++;
if (currentMonth === 13) {
currentMonth = 1;
currentYear++;
}
}
allMonths.forEach(month => {
if (!months.find(m => m.Month === month)) {
months.push({Month: month, count: 0});
}
});
console.log(months);

Javascript get sum of indices of n number of arrays of integers

I have an array of n number of objects, each which has an array ("needs") in this case. What I want to do is iterate through the array, and grab the sum of the index of each array.
const testData =
[{
location: 'FL',
needs:[{date: '2021-01-01', count: 5},{date: '2021-01-02', count: 1},{date: '2021-01-03', count: 2},{date: '2021-01-04', count: 23},{date: '2021-01-05', count: 65}]
},{
location: 'AL',
needs:[{date: '2021-01-01', count: 1},{date: '2021-01-02', count: 2},{date: '2021-01-03', count: 3},{date: '2021-01-04', count: 4},{date: '2021-01-05', count: 5}]
}]
So in this case, I would be left with an array that looks like [6, 3, 5, 27, 70] since testData[0].needs[0] + testData[1].needs[0] === 6 & testData[0].needs[1] + testData[1].needs[1] === 3, etc.
The function I came up with
testData.map((val, index) => {
val.needs.map((needs, index) => {
dayArray.push(needs.count)
})
})
is unfortunately doing testData[0].needs[0] + testData[0].needs[1], more or less the opposite of what I need. How do I tweak this function to get the expected results?
you can use a mapper to track the sum of date like this
const testData = [{
location: 'FL',
needs: [{
date: '2021-01-01',
count: 5
}, {
date: '2021-01-02',
count: 1
}, {
date: '2021-01-03',
count: 2
}, {
date: '2021-01-04',
count: 23
}, {
date: '2021-01-05',
count: 65
}]
}, {
location: 'AL',
needs: [{
date: '2021-01-01',
count: 1
}, {
date: '2021-01-02',
count: 2
}, {
date: '2021-01-03',
count: 3
}, {
date: '2021-01-04',
count: 4
}, {
date: '2021-01-05',
count: 5
}]
}]
const mapper = testData.reduce((acc, cur) => {
cur.needs.forEach(item => {
acc[item.date] = (acc[item.date] || 0) + item.count;
});
return acc;
}, {});
const dates = Object.keys(mapper);
dates.sort();
console.log(dates.map(k => mapper[k]));

Group array of objects by common key (date string)

I have an array as below.
const list = [
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}
]
And I want to sum number and count depends on same date. After that it should be merged in same date. Therefore I want to get the result as below.
const list = [{
totalNumber: 9,
totalCount: 6,
detail:[{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
}]
},
{
totalNumber: 8,
totalCount: 2,
detail:[
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}]
}
]
I know stackoverflow is not code writing site. But I made this from some data. But this is my last stage for completing my object. And I don't know how I can group like that. I'' appreciate if you help me to resolve this problem. Thank you so much for reading it.
To me, optimal approach is building Map (having necessary property as a key) with Array.prototype.reduce(), then extracting array of values (aggregated records) with Map.prototype.values():
const src = [{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-02",number:4,count:1},{date:"2020-01-02",number:4,count:1}],
groupped = [...src
.reduce((acc, {date, number, count}) => {
const group = acc.get(date)
if(group){
group.totalNumber += number
group.totalCount += count
group.detail.push({date, number, count})
} else {
acc.set(
date,
{
date,
totalNumber: number,
totalCount: count,
detail: [{
date,
number,
count
}]
}
)
}
return acc
}, new Map)
.values()
]
console.log(groupped)
.as-console-wrapper{min-height:100%;}

Group an array of time object

I have a requirement to group an array of objects based on time interval. The input looks like:
[
{
_id: {
hour: 0,
interval: '0'
},
time: '0:0',
count: 10
},
{
_id: {
hour: 0,
interval: '15'
},
time: '0:15',
count: 5
},
{
_id: {
hour: 0,
interval: '30'
},
time: '0:30',
count: 1
},
{
_id: {
hour: 0,
interval: '45'
},
time: '0:45',
count: 2
},
{
_id: {
hour: 1,
interval: '0'
},
time: '1:0',
count: 4
},
{
_id: {
hour: 1,
interval: '15'
},
time: '1:15',
count: 3
},
{
_id: {
hour: 1,
interval: '30'
},
time: '1:30',
count: 5
},
{
_id: {
hour: 1,
interval: '45'
},
time: '1:45',
count: 1
}
]
My desired output:
[
{
"time": "0",
"0": 10,
"15": 5
"30": 1,
"45": 2
},
{
"time": "1",
"0": 4,
"15": 3
"30": 5,
"45": 1
}
]
I tried to use the following code to group the objects, which works to an extent, but I'm stuck on what to do next:
const a = [ { _id: { hour: 0, interval: '0' }, time: '0:0', count: 10 }, { _id: { hour: 0, interval: '15' }, time: '0:15', count: 5 }, { _id: { hour: 0, interval: '30' }, time: '0:30', count: 1 }, { _id: { hour: 0, interval: '45' }, time: '0:45', count: 2 }, { _id: { hour: 1, interval: '0' }, time: '1:0', count: 4 }, { _id: { hour: 1, interval: '15' }, time: '1:15', count: 3 }, { _id: { hour: 1, interval: '30' }, time: '1:30', count: 5 }, { _id: { hour: 1, interval: '45' }, time: '1:45', count: 1 }]
var group = a.reduce((r, a) => {
console.log("a", a);
console.log('r', r);
r[a._id.hour] = [...r[a._id.hour] || [], a];
return r;
}, {});
console.log("group", group);
Check if the object with that hour exists in the accumulator object first - if it doesn't, create one, then assign count to that object's [interval] property, and get the Object.values at the end to turn it back into an array:
const input=[{_id:{hour:0,interval:"0"},time:"0:0",count:10},{_id:{hour:0,interval:"15"},time:"0:15",count:5},{_id:{hour:0,interval:"30"},time:"0:30",count:1},{_id:{hour:0,interval:"45"},time:"0:45",count:2},{_id:{hour:1,interval:"0"},time:"1:0",count:4},{_id:{hour:1,interval:"15"},time:"1:15",count:3},{_id:{hour:1,interval:"30"},time:"1:30",count:5},{_id:{hour:1,interval:"45"},time:"1:45",count:1}];
const groupedObj = {};
for (const { _id: { hour, interval }, count } of input) {
if (!groupedObj[hour]) {
groupedObj[hour] = { time: hour };
}
groupedObj[hour][interval] = count;
}
const output = Object.values(groupedObj);
console.log(output);
Reduce the array, and create an object for each _id.time. Assign the current [interval] = count to the object. Get the entries, and use Array.from() to convert the entries to an array of the required form:
const arr = [{"_id":{"hour":0,"interval":"0"},"time":"0:0","count":10},{"_id":{"hour":0,"interval":"15"},"time":"0:15","count":5},{"_id":{"hour":0,"interval":"30"},"time":"0:30","count":1},{"_id":{"hour":0,"interval":"45"},"time":"0:45","count":2},{"_id":{"hour":1,"interval":"0"},"time":"1:0","count":4},{"_id":{"hour":1,"interval":"15"},"time":"1:15","count":3},{"_id":{"hour":1,"interval":"30"},"time":"1:30","count":5},{"_id":{"hour":1,"interval":"45"},"time":"1:45","count":1}];
// convert the entries to an array
const result = Array.from(Object.entries(
arr.reduce((r, o) => {
const { hour, interval } = o._id; // get the hour and interval
if(!r[hour]) r[hour] = {}; // create a the hour object
r[hour][interval] = o.count; // add the interval and count
return r;
}, {})
), ([time, values]) => ({ time, ...values })); // generate the result objects
console.log(result)
You can group object by reduce method. So at first you need to group by hour and then just add interval properties from each iteration of reduce method to the hour property:
const result = arr.reduce((a, c) => {
a[c._id.hour] = a[c._id.hour] || {};
a[c._id.hour].time = c._id.hour;
a[c._id.hour][c._id.interval] = c.count;
return a;
}, {})
console.log(result);
An example:
let arr = [
{
_id: {
hour: 0,
interval: '0'
},
time: '0:0',
count: 10
},
{
_id: {
hour: 0,
interval: '15'
},
time: '0:15',
count: 5
},
{
_id: {
hour: 0,
interval: '30'
},
time: '0:30',
count: 1
},
{
_id: {
hour: 0,
interval: '45'
},
time: '0:45',
count: 2
},
{
_id: {
hour: 1,
interval: '0'
},
time: '1:0',
count: 4
},
{
_id: {
hour: 1,
interval: '15'
},
time: '1:15',
count: 3
},
{
_id: {
hour: 1,
interval: '30'
},
time: '1:30',
count: 5
},
{
_id: {
hour: 1,
interval: '45'
},
time: '1:45',
count: 1
}
]
const result = arr.reduce((a, c) => {
a[c._id.hour] = a[c._id.hour] || {};
a[c._id.hour].time = c._id.hour;
a[c._id.hour][c._id.interval] = c.count;
return a;
}, {})
console.log(result);

Efficiently pull unknown data, separate, and store in specific format?

I've got data I need to sort through, filter, and store in a specific way. I'll explain by showing. Here is the data:
var pieData, cakeData, icecreamData;
var desserts = [
{
pies: [
{
name: "blueberry",
count: 3
},
{
name: "pumpkin",
count: 6
},
{
name: "apple",
count: 9
}
],
cakes: [
{
name: "chocolate",
count: 3
},
{
name: "foam",
count: 6
},
{
name: "wedding",
count: 9
}
],
icecream: [
{
name: "chocolate",
count: 3
},
{
name: "strawberry",
count: 6
},
{
name: "mint-chip",
count: 9
}
],
date: "2016-01-06T00:00:00"
},
{
pies: [
{
name: "blueberry",
count: 2
},
{
name: "pumpkin",
count: 4
},
{
name: "apple",
count: 6
}
],
cakes: [
{
name: "chocolate",
count: 2
},
{
name: "foam",
count: 4
},
{
name: "wedding",
count: 6
}
],
icecream: [
{
name: "chocolate",
count: 2
},
{
name: "strawberry",
count: 4
},
{
name: "mint-chip",
count: 6
}
],
date: "2016-01-07T00:00:00"
},
{
pies: [
{
name: "blueberry",
count: 4
},
{
name: "pumpkin",
count: 8
},
{
name: "apple",
count: 12
}
],
cakes: [
{
name: "chocolate",
count: 4
},
{
name: "foam",
count: 8
},
{
name: "wedding",
count: 12
}
],
icecream: [
{
name: "chocolate",
count: 4
},
{
name: "strawberry",
count: 8
},
{
name: "mint-chip",
count: 12
}
],
date: "2016-01-08T00:00:00"
}
];
So I've got my data. The data is basically what types of pies, cakes, and icereams there are which can vary in number, name, and count. Each object in desserts is a day, with the date as the last property. I'll go straight to what I want to get out of it and then explain further after that. Here is what I need to get out of it:
pieData = [
{
name: "blueberry",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [3, 2, 4]
},
{
name: "pumpkin",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [6, 4, 8]
},
{
name: "apple",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [9, 6, 12]
}
];
cakeData = [
{
name: "chocolate",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [3, 2, 4]
},
{
name: "foam",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [6, 4, 8]
},
{
name: "wedding",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [9, 6, 12]
}
];
icecreamData = [
{
name: "chocolate",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [3, 2, 4]
},
{
name: "strawberry",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [6, 4, 8]
},
{
name: "mint-chip",
dates: ["2016-01-06T00:00:00", "2016-01-07T00:00:00", "2016-01-08T00:00:00"],
counts: [9, 6, 12]
}
];
So I need to pass the desserts variable to a function and have it set the pieData, cakeData, and icecreamData variables, declared at the top of the original data, to the array of objects I've shown in the second bit of code.
A Few Things to Note:
In the output data, the values correspond to the days they were originally assigned in the original data. For example, pieData[0].dates[0] corresponds to pieData[0].counts[0] as it appears in the original data.
There could be infinite types of each dessert or none. Meaning there could be 10 different objects in the "pie" array in the original data or none. But there will always be pies, cakes, and icecream arrays, whether they are empty or have a thousand objects in them.
I don't know what types of each dessert will come through. It could be anything. So the pie could be named "pumpkin" or it could be named "battery acid".
My only solution I could think of was to do multiple loops and nested loops and just overall too much code. I know there has to be some efficient wizardry to get this done right.
I understand the drive to want "minified" code, but I think it's best to keep things readable. Despite your reservations, I think a few nested loops make sense in this case.
Consider the following way of getting the desired result:
var pieData, cakeData, icecreamData;
var desserts = [{pies:[{name:"blueberry",count:3},{name:"pumpkin",count:6},{name:"apple",count:9}],cakes:[{name:"chocolate",count:3},{name:"foam",count:6},{name:"wedding",count:9}],icecream:[{name:"chocolate",count:3},{name:"strawberry",count:6},{name:"mint-chip",count:9}],date:"2016-01-06T00:00:00"},{pies:[{name:"blueberry",count:2},{name:"pumpkin",count:4},{name:"apple",count:6}],cakes:[{name:"chocolate",count:2},{name:"foam",count:4},{name:"wedding",count:6}],icecream:[{name:"chocolate",count:2},{name:"strawberry",count:4},{name:"mint-chip",count:6}],date:"2016-01-07T00:00:00"},{pies:[{name:"blueberry",count:4},{name:"pumpkin",count:8},{name:"apple",count:12}],cakes:[{name:"chocolate",count:4},{name:"foam",count:8},{name:"wedding",count:12}],icecream:[{name:"chocolate",count:4},{name:"strawberry",count:8},{name:"mint-chip",count:12}],date:"2016-01-08T00:00:00"}];
var dessertData= {};
for (var dessertGroup of desserts) {
for (var item in dessertGroup) {
// the timestamp is not a dessert, so skip it
if (item === 'date') { continue; }
if (!dessertData[item]) {
// this is a new kind of dessert, add it
dessertData[item] = [];
}
for (var flavour of dessertGroup[item]) {
// get the index of the flavour
var index = dessertData[item].map(function(e) { return e.name; }).indexOf(flavour.name);
if (index < 0) {
// this is a new flavour of dessert, add it to the dessert type
dessertData[item].push({
name: flavour.name,
dates: [],
counts: []
});
index = dessertData[item].length - 1;
}
// append the relevant data to the flavour properties
dessertData[item][index].dates.push(dessertGroup.date);
dessertData[item][index].counts.push(flavour.count);
}
}
}
// finally, we want 'cakes' in 'cakeData'
// 'pies' in 'pieData'
// and 'icecream in 'icecreamData'
cakeData = dessertData.cakes;
pieData = dessertData.pies;
icecreamData = dessertData.icecream;
console.log("cakeData=", cakeData);
console.log("pieData=", pieData);
console.log("icecreamData=", icecreamData);
This is easy to read and modify. Also, it allows for any type of dessert! Why limit yourself to pies, cake, and icecream.
You'll notice that I'm dynamically creating and accessing the properties of dessertData by doing stuff like dessertData[propertyName].
Maybe you knew that was possible, but I used javascript for a long time before learning that the [] syntax wasn't just for numerical indices. Good luck!
This converts it into the format you want
https://jsfiddle.net/sdhjL7dv/
var pieData = [], cakeData = [], icecreamData = [];
var desserts = [
{
pies: [
{
name: "blueberry",
count: 3
},
{
name: "pumpkin",
count: 6
},
{
name: "apple",
count: 9
}
],
cakes: [
{
name: "chocolate",
count: 3
},
{
name: "foam",
count: 6
},
{
name: "wedding",
count: 9
}
],
icecream: [
{
name: "chocolate",
count: 3
},
{
name: "strawberry",
count: 6
},
{
name: "mint-chip",
count: 9
}
],
date: "2016-01-06T00:00:00"
},
{
pies: [
{
name: "blueberry",
count: 2
},
{
name: "pumpkin",
count: 4
},
{
name: "apple",
count: 6
}
],
cakes: [
{
name: "chocolate",
count: 2
},
{
name: "foam",
count: 4
},
{
name: "wedding",
count: 6
}
],
icecream: [
{
name: "chocolate",
count: 2
},
{
name: "strawberry",
count: 4
},
{
name: "mint-chip",
count: 6
}
],
date: "2016-01-07T00:00:00"
},
{
pies: [
{
name: "blueberry",
count: 4
},
{
name: "pumpkin",
count: 8
},
{
name: "apple",
count: 12
}
],
cakes: [
{
name: "chocolate",
count: 4
},
{
name: "foam",
count: 8
},
{
name: "wedding",
count: 12
}
],
icecream: [
{
name: "chocolate",
count: 4
},
{
name: "strawberry",
count: 8
},
{
name: "mint-chip",
count: 12
}
],
date: "2016-01-08T00:00:00"
}
];
for(var i = 0; i < desserts.length; i++) {
var d = desserts[i].date;
desserts[i].pies.length && save(pieData, desserts[i].pies, d);
desserts[i].cakes.length && save(cakeData, desserts[i].cakes, d);
desserts[i].icecream.length && save(icecreamData, desserts[i].icecream, d);
}
function save(destination, items, d) {
for(var i = 0; i < items.length; i++) {
var name = items[i].name;
var count = items[i].count;
if(destination[name] === undefined) { destination[name] = {name:'',dates:[],counts:[]}; }
destination[name].name = name;
destination[name].dates.push(d);
destination[name].counts.push(count);
}
}
console.log(pieData);
console.log(cakeData);
console.log(icecreamData);

Categories

Resources