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);
Related
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);
i'm trying to get some data together based on some data.
So for example as seen below the items array displays the total amount of times a number is between the range in the data, so for example 4500 - 5000 has got 4 numbers between that range.
However adding these up doesn't give an accurate representation of the amount of items there is. If this makes sense? As in the result the count for 7000 is 13, however this should be 8.
const data = [ 2200, 3900, 4500, 4500, 4800, 5000, 6000, 6000 ]
const items = [
{ value: { from: 2000, to: 2500 }, count: null },
{ value: { from: 2500, to: 3000 }, count: null },
{ value: { from: 3000, to: 3500 }, count: null },
{ value: { from: 3500, to: 4000 }, count: null },
{ value: { from: 4000, to: 4500 }, count: null },
{ value: { from: 4500, to: 5000 }, count: null },
{ value: { from: 5000, to: 5500 }, count: null },
{ value: { from: 5500, to: 6000 }, count: null },
{ value: { from: 6000, to: 6500 }, count: null },
{ value: { from: 6500, to: 7000 }, count: null },
].map(item => {
item.count = data.filter(x => x <= item.value.to && x >= item.value.from).length;
return item;
});
let counter = 0;
result = items.map(i => {
counter += i.count;
return { value: i.value.to, label: i.value.to, count: counter }
}).filter(Boolean);
console.log({items, result});
What I SHOULD have is the following, as this data is for an UPTO dropdown.
[
{ value: { from: 2000, to: 2500 }, count: 1 },
{ value: { from: 2500, to: 3000 }, count: 1 },
{ value: { from: 3000, to: 3500 }, count: 1 },
{ value: { from: 3500, to: 4000 }, count: 2 },
{ value: { from: 4000, to: 4500 }, count: 4 },
{ value: { from: 4500, to: 5000 }, count: 6 },
{ value: { from: 5000, to: 5500 }, count: 6 },
{ value: { from: 5500, to: 6000 }, count: 8 },
{ value: { from: 6000, to: 6500 }, count: 8 },
{ value: { from: 6500, to: 7000 }, count: 8 },
]
You should not consider both from and to inside the boundary. This will result in having common elements in individual groups.
For example: From 4000 to 4500 means numbers greater than 4000 and less than or equal to 4500.
Here in the above example, the lover boundary donot overlaps, so that the individual groups donot have common elements.
const data = [2200, 3900, 4500, 4500, 4800, 5000, 6000, 6000]
const items = [
{ value: { from: 2000, to: 2500 }, count: null },
{ value: { from: 2500, to: 3000 }, count: null },
{ value: { from: 3000, to: 3500 }, count: null },
{ value: { from: 3500, to: 4000 }, count: null },
{ value: { from: 4000, to: 4500 }, count: null },
{ value: { from: 4500, to: 5000 }, count: null },
{ value: { from: 5000, to: 5500 }, count: null },
{ value: { from: 5500, to: 6000 }, count: null },
{ value: { from: 6000, to: 6500 }, count: null },
{ value: { from: 6500, to: 7000 }, count: null },
].map(item => {
item.count = data.filter(x => x <= item.value.to && x > item.value.from).length;
return item;
});
let counter = 0;
result = items.map(i => {
counter += i.count;
return { value: i.value.to, label: i.value.to, count: counter }
}).filter(Boolean);
console.log({ items, result });
You could take a single loop and map a new object with count.
const
data = [2200, 3900, 4500, 4500, 4800, 5000, 6000, 6000],
values = [{ value: { from: 2000, to: 2500 }, count: null }, { value: { from: 2500, to: 3000 }, count: null }, { value: { from: 3000, to: 3500 }, count: null }, { value: { from: 3500, to: 4000 }, count: null }, { value: { from: 4000, to: 4500 }, count: null }, { value: { from: 4500, to: 5000 }, count: null }, { value: { from: 5000, to: 5500 }, count: null }, { value: { from: 5500, to: 6000 }, count: null }, { value: { from: 6000, to: 6500 }, count: null }, { value: { from: 6500, to: 7000 }, count: null }],
items = values.map(item => ({
...item,
count: data.reduce((s, x) => s + (x <= item.value.to), 0)
}));
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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]));
I have an array of objects such as:
const array = [
{
date: '02-02-1994',
time: '18:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-02-1994',
time: '20:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 1,
second: 2
}
}
]
I want to group it by time and date and get the result array:
const result = [{
date: '02-02-1994',
time: '18:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-02-1994',
time: '20:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 2,
second: 3
}
}]
I want to group array by date then by time and to get a sum in service object.
I try to group by date using reduce, but then I don't know
You can use reduce() method to do that.
const array = [{ date: '02-02-1994', time: '18:00', services: { first: 1, second: 1 } }, { date: '02-02-1994', time: '20:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 2 } } ];
let result = array.reduce((arr, currentValue) => {
let item = arr.find(item =>
item.date === currentValue.date &&
item.time === currentValue.time);
if (item) {
item.services.first += currentValue.services.first;
item.services.second += currentValue.services.second;
} else {
arr.push(currentValue);
}
return arr;
}, []);
console.log(result);
You could take a joined key for collecting items of the same group in an object and then take the values of it as result set.
var array = [{ date: '02-02-1994', time: '18:00', services: { first: 1, second: 1 } }, { date: '02-02-1994', time: '20:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 2 } }],
result = Object.values(array.reduce((r, { date, time, services }) => {
var key = [date, time].join('|');
r[key] = r[key] || { date, time, services: {} };
Object
.entries(services)
.forEach(([k, v]) => r[key].services[k] = (r[key].services[k] || 0) + v);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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];
)