Is there someone who is familiar with functional programming in javascript that can help me parse this data from google place API:
`periods: [
{ close: { day: 1, time: '1400' }, open: { day: 1, time: '1100' } },
{ close: { day: 1, time: '2200' }, open: { day: 1, time: '1900' } },
{ close: { day: 2, time: '1400' }, open: { day: 2, time: '1100' } },
{ close: { day: 2, time: '2200' }, open: { day: 2, time: '1900' } },
{ close: { day: 3, time: '1400' }, open: { day: 3, time: '1100' } },
{ close: { day: 3, time: '2200' }, open: { day: 3, time: '1900' } },
{ close: { day: 4, time: '1400' }, open: { day: 4, time: '1100' } },
{ close: { day: 4, time: '2200' }, open: { day: 4, time: '1900' } },
{ close: { day: 5, time: '1400' }, open: { day: 5, time: '1100' } },
{ close: { day: 5, time: '2200' }, open: { day: 5, time: '1900' } },
{ close: { day: 6, time: '2200' }, open: { day: 6, time: '1100' } },
];`
so each entry in the array is an object. The object contains open and close object that represent the day of the week and opening/closing hour.
This place is open from
11:00 - 14:00 and 19:00 - 22:00 during weekdays.
11:00 - 22:00 on Saturdays.
Closed on Sundays because no entry has got the day : 0.
How can I use functional programming to parse this array into an array like this:
`openingHours = [
"Sundays: Closed"
"Mondays: 11:00 - 14:00 and 19:00 - 22:00",
"Tuesdays: 11:00 - 14:00 and 19:00 - 22:00",
"Wednesdays: 11:00 - 14:00 and 19:00 - 22:00",
"Thursdays: 11:00 - 14:00 and 19:00 - 22:00",
"Fridays: 11:00 - 14:00 and 19:00 - 22:00",
"Saturdays: 11:00 - 22:00"
]`
My attempt: https://jsfiddle.net/4u53tb1p/1/
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const openingHours =
periods.map(p => ({
day: p.open.day,
time: `${p.open.time} - ${p.close.time}`
}))
.reduce((acc, current) => {
let time = acc[current.day];
time.push(current.time);
return Object.assign([], acc, { [current.day]: time });
}, days.map(d => []))
.map((p, index) => {
const status = p.length == 0 ? "Closed" : p.join(" and ");
return `${days[index]}: ${status}`;
});
Breakdown:
The first map() convert from initial structure to:
[
{day: 1, time: "1100 - 1400"},
{day: 1, time: "1900 - 2200"},
{day: 2, time: "1100 - 1400"},
{day: 2, time: "1900 - 2200"},
{day: 3, time: "1100 - 1400"},
{day: 3, time: "1900 - 2200"},
{day: 4, time: "1100 - 1400"},
{day: 4, time: "1900 - 2200"},
{day: 5, time: "1100 - 1400"},
{day: 5, time: "1900 - 2200"},
{day: 6, time: "1100 - 2200"}
]
The reduce takes objects that have the same day number, group them together (.i.e. putting their time in the same array):
[
[],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 2200"]
]
Notice that I provide a nested array with the same length as days array to the reducer() function as initial accumulator. This is to pad the previous map() output with any day that doesn't have entry (.i.e. Sunday in this case). Sunday doesn't have any entry, so at the end of reduce(), the value is empty array
And final step is to transform the previous nested array into desired output, which is quite straightforward: map the index to days array to get the label of the day
This solution is not using any 3rd party library
Here is a solution.
First we have to add a MyGroupBy function
var groupBy = function(xs, key1, key2) {
return xs.reduce(function(rv, x) {
(rv[x[key1][key2]] = rv[x[key1][key2]] || []).push(x);
return rv;
}, {});
};
let periods = [
{ close: { day: 1, time: '1400' }, open: { day: 1, time: '1100' } },
{ close: { day: 1, time: '2200' }, open: { day: 1, time: '1900' } },
{ close: { day: 2, time: '1400' }, open: { day: 2, time: '1100' } },
{ close: { day: 2, time: '2200' }, open: { day: 2, time: '1900' } },
{ close: { day: 3, time: '1400' }, open: { day: 3, time: '1100' } },
{ close: { day: 3, time: '2200' }, open: { day: 3, time: '1900' } },
{ close: { day: 4, time: '1400' }, open: { day: 4, time: '1100' } },
{ close: { day: 4, time: '2200' }, open: { day: 4, time: '1900' } },
{ close: { day: 5, time: '1400' }, open: { day: 5, time: '1100' } },
{ close: { day: 5, time: '2200' }, open: { day: 5, time: '1900' } },
{ close: { day: 6, time: '2200' }, open: { day: 6, time: '1100' } },
];
invok our function "MyGroupBy"
openingHoursArr = myGroupBy(periods, 'open', 'day');
openingHours must be an object
let openingHours = {
"Sundays": openingHoursArr[0] ? openingHoursArr[0].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Mondays": openingHoursArr[1] ? openingHoursArr[1].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Tuesdays": openingHoursArr[2] ? openingHoursArr[2].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Wednesdays": openingHoursArr[3] ? openingHoursArr[3].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Thursdays": openingHoursArr[4] ? openingHoursArr[4].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Fridays": openingHoursArr[5] ? openingHoursArr[5].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed",
"Saturdays": openingHoursArr[6] ? openingHoursArr[6].map( i => i.open.time+ ' - ' +i.close.time ).join(' and ') : "Closed"
};
enjoy.
Here's another options...
var data = {periods: [
{ close: { day: 1, time: '1400' }, open: { day: 1, time: '1100' } },
{ close: { day: 1, time: '2200' }, open: { day: 1, time: '1900' } },
{ close: { day: 2, time: '1400' }, open: { day: 2, time: '1100' } },
{ close: { day: 2, time: '2200' }, open: { day: 2, time: '1900' } },
{ close: { day: 3, time: '1400' }, open: { day: 3, time: '1100' } },
{ close: { day: 3, time: '2200' }, open: { day: 3, time: '1900' } },
{ close: { day: 4, time: '1400' }, open: { day: 4, time: '1100' } },
{ close: { day: 4, time: '2200' }, open: { day: 4, time: '1900' } },
{ close: { day: 5, time: '1400' }, open: { day: 5, time: '1100' } },
{ close: { day: 5, time: '2200' }, open: { day: 5, time: '1900' } },
{ close: { day: 6, time: '2200' }, open: { day: 6, time: '1100' } },
]};
var days = "Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday".split(", ");
var hours = {};
days.map(itm => hours[itm] = `${itm}s: Closed`);
data = data.periods.map(itm => ({
day: days[itm.close.day],
open: itm.open.time.match(/\d{2}/g).join(":"),
close: itm.close.time.match(/\d{2}/g).join(":")
})).reduce((acc, itm) => {
let str = `${itm.day}s: ${itm.open} - ${itm.open}`;
acc[itm.day] = ~acc[itm.day].indexOf('Closed') ? str : acc[itm.day] + ` and ${str}`;
return acc;
}, hours);
data = days.map(itm => data[itm]);
console.log(data);
Here's yet another options...
const DAYS =
[ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]
const format = (open, close) =>
`${open.time} - ${close.time}`
const main = (periods) =>
{
const hours =
periods.reduce ((m, { open, close }) =>
m.has (open.day)
? m.set (open.day, [ ...m.get (open.day), format (open, close) ])
: m.set (open.day, [ format (open, close) ])
, new Map ())
return DAYS.map ((name, day) =>
hours.has (day)
? name + ': ' + hours.get (day) .join (' and ')
: name + ': ' + 'Closed')
}
const data =
[ { close : { day : 1, time : "1400" }, open : { day : 1, time : "1100" } }
, { close : { day : 1, time : "2200" }, open : { day : 1, time : "1900" } }
, { close : { day : 2, time : "1400" }, open : { day : 2, time : "1100" } }
, { close : { day : 2, time : "2200" }, open : { day : 2, time : "1900" } }
, { close : { day : 3, time : "1400" }, open : { day : 3, time : "1100" } }
, { close : { day : 3, time : "2200" }, open : { day : 3, time : "1900" } }
, { close : { day : 4, time : "1400" }, open : { day : 4, time : "1100" } }
, { close : { day : 4, time : "2200" }, open : { day : 4, time : "1900" } }
, { close : { day : 5, time : "1400" }, open : { day : 5, time : "1100" } }
, { close : { day : 5, time : "2200" }, open : { day : 5, time : "1900" } }
, { close : { day : 6, time : "2200" }, open : { day : 6, time : "1100" } }
]
console.log (main (data))
This solution is not using any 3rd party library 📚
So here is how I solved it. I used the solution from #Phuong Nguyen.
I also found out that if a place is open 24/7 then the data is represented as:
`periods = [
{open: {day: 0, time: "0000"}}
]`
So I had to add that into the getOpeningHours function.
Because the application is in Icelandic the days are in Icelandic (you can learn a little bit of Icelandic if you like)
`const days = [
'Sunnudagar',
'Mánudagar',
'Þriðjudagar',
'Miðvikudagar',
'Fimmtudagar',
'Föstudagar',
'Laugardagar',
];`
`const createTimeStrings = item => {
return {
day: item.open.day,
time: ``${item.open.time
.split('')
.reduce(addColon, [])
.join('')} - ${item.close.time
.split('')
.reduce(addColon, [])
.join('')}``,
};
};`
`const addColon = (acc, curr, index) =>
index === 2 ? [...acc, ':', curr] : [...acc, curr];`
This function changes the data structure to:
`[
{day: 1, time: "11:00 - 14:00"},
{day: 1, time: "19:00 - 22:00"},
{day: 2, time: "11:00 - 14:00"},
]`
`const getHoursbyDay = (acc, curr) => {
let time = acc[curr.day];
let newTime = [...time, curr.time];
return [...acc.slice(0, curr.day), newTime, ...acc.slice(curr.day + 1)];
};`
This function changes the data to :
`[
[],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 1400", "1900 - 2200"],
["1100 - 2200"]
]`
The main function that uses the other helper functions and deals with when places are open 24/7
`export const getOpeningHours = periods => {
/* Clients can rely on always-open being represented
as an open period containing day with value 0 and
time with value 0000, and no close.
*/
if (
periods.length === 1 &&
periods[0].open.day === 0 &&
periods[0].open.time === '0000'
) {
return ['Opið allan sólarhringinn alla daga'];
}
// otherwise, parse openinghours
return periods
.map(createTimeStrings)
.reduce(getHoursbyDay, days.map(d => []))
.map((arr, index) => {
const hours = arr.length === 0 ? 'Lokað' : arr.join(' og ');
return `${days[index]}: ${hours}`;
});
};`
Related
I have an array of objects that I want to transform. My dataset looks like this:
[
{
day: sunday,
val: 20
},
{
day: sunday,
val: 20
},
{
day: monday,
val: 10
},
{
day: monday,
val: 30
},
{
day: tuesday,
val: 5
},
{
day: tuesday,
val: 5
}
]
I am trying to transform the data to look like this:
Output:
[[20,20], [10,30], [5, 5]]
Where each of the nested arrays are based on the Day of Week in object. Any ideas?
Thanks!
You could group your items by their day. After you have the groups, you can grab the values of the map and map the item lists to a list of val.
const data = [
{ day: 'sunday' , val: 20 }, { day: 'sunday' , val: 20 },
{ day: 'monday' , val: 10 }, { day: 'monday' , val: 30 },
{ day: 'tuesday' , val: 5 }, { day: 'tuesday' , val: 5 }
];
const transformed = Object.values(data.reduce((map, item) =>
({ ...map, [item.day] : [ ...(map[item.day] || []), item]
}), {})).map(list => list.map(item => item.val));
console.log(transformed);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Alternatively, you can reduce the values right away, but you lose all the item properties.
const data = [
{ day: 'sunday' , val: 20 }, { day: 'sunday' , val: 20 },
{ day: 'monday' , val: 10 }, { day: 'monday' , val: 30 },
{ day: 'tuesday' , val: 5 }, { day: 'tuesday' , val: 5 }
];
const transformed = Object.values(data.reduce((map, item) =>
({ ...map, [item.day] : [ ...(map[item.day] || []), item.val] }), {}));
console.log(transformed);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Here is a functional version:
const data = [
{ day: 'sunday' , val: 20 }, { day: 'sunday' , val: 20 },
{ day: 'monday' , val: 10 }, { day: 'monday' , val: 30 },
{ day: 'tuesday' , val: 5 }, { day: 'tuesday' , val: 5 }
];
const toMatrix = (list, key, valFn) => Object.values(data.reduce((map, item) =>
({ ...map, [item[key]] : [ ...(map[item[key]] || []), valFn(item) ] }), {}))
console.log(toMatrix(data, 'day', item => item.val));
.as-console-wrapper { top: 0; max-height: 100% !important; }
let myarray=[
{
day: 'sunday',
val: 20
},
{
day: 'sunday',
val: 20
},
{
day: 'monday',
val: 10
},
{
day: 'monday',
val: 30
},
{
day: 'tuesday',
val: 5
},
{
day: 'tuesday',
val: 5
}
];
let newarray=[];
for (let x of myarray) {
if (!newarray[x.day]) { newarray[x.day]=[]; }
newarray[x.day].push(x.val);
}
console.log(newarray);
This could be a solution:
var obj = [
{
day: 'sunday',
val: 20
},
{
day: 'sunday',
val: 20
},
{
day: 'monday',
val: 10
},
{
day: 'monday',
val: 30
},
{
day: 'tuesday',
val: 5
},
{
day: 'tuesday',
val: 5
}
]
var obj_output = [];
var bln_array = false;
for (i=0; i < obj.length; i++) {
var arr = [];
arr.push(obj[i].val);
arr.push(obj[i+1].val);
obj_output.push(arr);
i = i+1
}
console.log(obj_output);
Here's one way to achieve this
var old = [
{ day: 'sunday', val: 20 },
{ day: 'sunday', val: 20 },
{ day: 'monday', val: 10 },
{ day: 'monday', val: 30 },
{ day: 'tuesday', val: 5 },
{ day: 'tuesday', val: 5 }
];
var days = [];
var result = [];
old.forEach(o => {
if(days.indexOf(o.day) === -1) {
days.push(o.day);
result.push([]);
}
result[days.indexOf(o.day)].push(o.val);
});
console.log(result);
Here is an example on stackblitz on how you could achieve that, and here is the code :
import React, { Component } from "react";
import { render } from "react-dom";
import * as _ from "lodash";
const App = () => {
const data = [
{
day: "sunday",
val: 20
},
{
day: "sunday",
val: 20
},
{
day: "monday",
val: 10
},
{
day: "monday",
val: 30
},
{
day: "tuesday",
val: 5
},
{
day: "tuesday",
val: 5
}
];
const groupBy = (arr, prop) => {
const map = new Map(Array.from(arr, obj => [obj[prop], []]));
arr.forEach(obj => map.get(obj[prop]).push(obj.val));
return Array.from(map.values());
};
React.useEffect(() => {
// groupBy from Lodash does not do exaclty what you want (but still very interesting), need to work on it a bit
let chunks = Object.values(_.groupBy(data, 'day')).map(x => x.map(item => item.val));
console.log(chunks);
// Exactly what you want, but custom made by someone here :
// https://stackoverflow.com/a/53704154/9868549
console.log(groupBy(data, 'day'));
}, []);
return <div>This is a template react</div>;
};
render(<App />, document.getElementById("root"));
I found it here on another StackOverflow thread but still wanted to provide another solution with lodash.
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);
Hard to set a title and a description for this one, but you'll get it when u read the code and comments, hopefully. If you guys got better idea please edit.
How can i get, in javascript, the timestamp sum difference between each true and false for each day for this particular example?
As a general rule for the array: is it always loops true - false.
My real data is a bit more complex, but i just can't seem to get the thinking right, even for this simplified example.
Can be reduce, can be a for loop, anything. Thank you!
const data = [
{ day: 'today', timestamp: 11, value: true },
{ day: 'today', timestamp: 13, value: false }, //here should be 13-11
{ day: 'today', timestamp: 14, value: true },
{ day: 'today', timestamp: 17, value: false }, //here should be 17-14
//the sum for today should be 5 (13-11 + 17-14)
{ day: 'tomorrow', timestamp: 9, value: true },
{ day: 'tomorrow', timestamp: 11, value: false }, //here should be 11-9
{ day: 'tomorrow', timestamp: 11, value: true },
{ day: 'tomorrow', timestamp: 16, value: false } //here should be 16-11
//the sum for today should be 7 (11-9 + 16-11)
]
A simple reduce would make the job: just add the 'false' values which are bigger, and remove from the sum the 'true' values and you got it ;)
const data = [
{ day: 'today', timestamp: 11, value: true },
{ day: 'today', timestamp: 13, value: false }, //here should be 13-11
{ day: 'today', timestamp: 14, value: true },
{ day: 'today', timestamp: 17, value: false }, //here should be 17-14
//the sum for today should be 5 (13-11 + 17-14)
{ day: 'tomorrow', timestamp: 9, value: true },
{ day: 'tomorrow', timestamp: 11, value: false }, //here should be 11-9
{ day: 'tomorrow', timestamp: 11, value: true },
{ day: 'tomorrow', timestamp: 16, value: false } //here should be 16-11
//the sum for today should be 7 (11-9 + 16-11)
];
const result = data.reduce((acc, elt) => {
if(!acc[elt.day]) acc[elt.day] = 0;
if(!elt.value) {
acc[elt.day] += elt.timestamp;
} else {
acc[elt.day] -= elt.timestamp;
}
return acc;
},{});
console.log(result);
Do not forget to init each day to 0 in the accumulator!
Hoping this will help.
You can do it with reduce easily
const data = [
{ day: 'today', timestamp: 11, value: true },
{ day: 'today', timestamp: 13, value: false },
{ day: 'today', timestamp: 14, value: true },
{ day: 'today', timestamp: 17, value: false },
{ day: 'tomorrow', timestamp: 9, value: true },
{ day: 'tomorrow', timestamp: 11, value: false },
{ day: 'tomorrow', timestamp: 11, value: true },
{ day: 'tomorrow', timestamp: 16, value: false }
]
const op = data.reduce((o,c)=>{
if(o[c['day']]){
o[c['day']]['timestamp'] += c.value ? -c.timestamp : c.timestamp;
} else {
o[c['day']] = {
'timestamp' : c.value ? -c.timestamp : c.timestamp
}
}
return o;
},{})
console.log(op)
You could take a check with the last inserted object in the result set and update timestamp.
const
data = [{ day: 'today', timestamp: 11, value: true }, { day: 'today', timestamp: 13, value: false }, { day: 'today', timestamp: 14, value: true }, { day: 'today', timestamp: 17, value: false }, { day: 'tomorrow', timestamp: 9, value: true }, { day: 'tomorrow', timestamp: 11, value: false }, { day: 'tomorrow', timestamp: 11, value: true }, { day: 'tomorrow', timestamp: 16, value: false }],
result = data.reduce((r, { day, timestamp, value }) => {
var last = r[r.length - 1];
if (!last || last.day !== day) {
r.push(last = { day, timestamp: 0 });
}
last.timestamp += value ? -timestamp : +timestamp;
return r;
}, []);
console.log(result);
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];
)
I'm trying to synchronise two functions I run in my app.
First one checks the count of the documents I save to MongoDB every time block (e.g. every 10 seconds) in the real time:
var getVolume = function(timeBlock, cb) {
var triggerTime = Date.now();
var blockPeriod = triggerTime - timeBlock;
Document.find({
time: { $gt: blockPeriod }
}).count(function(err, count) {
log('getting volume since ', new Date(blockPeriod), 'result is', count)
cb(triggerTime, count);
});
};
and then I have the second function which I use whenever I want to get a data for my graph (front end):
var getHistory = function(timeBlock, end, cb) {
Document.aggregate(
{
$match: {
time: {
$gte: new Date(end - 10 * timeBlock),
$lt: new Date(end)
}
}
},
// count number of documents based on time block
// timeBlock is divided by 1000 as we use it as seconds here
// and the timeBlock parameter is in miliseconds
{
$group: {
_id: {
year: { $year: "$time" },
month: { $month: "$time" },
day: { $dayOfMonth: "$time" },
hour: { $hour: "$time" },
minute: { $minute: "$time" },
second: { $subtract: [
{ $second: "$time" },
{ $mod: [
{ $second: "$time" },
timeBlock / 1000
]}
]}
},
count: { $sum: 1 }
}
},
// changing the name _id to timeParts
{
$project: {
timeParts: "$_id",
count: 1,
_id: 0
}
},
// sorting by date, from earliest to latest
{
$sort: {
"time": 1
}
}, function(err, result) {
if (err) {
cb(err)
} else {
log("start", new Date(end - 10 * timeBlock))
log("end", new Date(end))
log("timeBlock", timeBlock)
log(">****", result)
cb(result)
}
})
}
and the problem is that I can't get the same values on my graph and on the back-end code (getVolume function)
I realised that the log from getHistory is not how I would expect it to be (log below):
start Fri Jul 18 2014 11:56:56 GMT+0100 (BST)
end Fri Jul 18 2014 11:58:36 GMT+0100 (BST)
timeBlock 10000
>**** [ { count: 4,
timeParts: { year: 2014, month: 7, day: 18, hour: 10, minute: 58, second: 30 } },
{ count: 6,
timeParts: { year: 2014, month: 7, day: 18, hour: 10, minute: 58, second: 20 } },
{ count: 3,
timeParts: { year: 2014, month: 7, day: 18, hour: 10, minute: 58, second: 10 } },
{ count: 3,
timeParts: { year: 2014, month: 7, day: 18, hour: 10, minute: 58, second: 0 } },
{ count: 2,
timeParts: { year: 2014, month: 7, day: 18, hour: 10, minute: 57, second: 50 } } ]
So I would expect that the getHistory should look up data in mongo every 10 seconds starting from start Fri Jul 18 2014 11:56:56 GMT+0100 (BST) so it will look roughly like:
11:56:56 count: 3
11:57:06 count: 0
11:57:16 count: 14
... etc.
TODO:
1. I know I should cover in my aggregate function the case when the count is 0 at the moment I guess this time is skipped`
Your error is how you're calculating _id for $group operator, specifically its second part:
second: { $subtract: [
{ $second: "$time" },
{ $mod: [
{ $second: "$time" },
timeBlock / 1000
]}
]}
So, instead of splitting all your data into 10 timeBlock milliseconds long chunks starting from new Date(end - 10 * timeBlock), you're splitting it into 11 chunks starting from from the nearest divisor of timeBlock.
To fix it you should first calculate delta = end - $time and then use it instead of the original $time to build your _id.
Here is an example of what I mean:
Document.aggregate({
$match: {
time: {
$gte: new Date(end - 10 * timeBlock),
$lt: new Date(end)
}
}
}, {
$project: {
time: 1,
delta: { $subtract: [
new Date(end),
"$time"
]}
}
}, {
$project: {
time: 1,
delta: { $subtract: [
"$delta",
{ $mod: [
"$delta",
timeBlock
]}
]}
}
}, {
$group: {
_id: { $subtract: [
new Date(end),
"$delta"
]},
count: { $sum: 1 }
}
}, {
$project: {
time: "$_id",
count: 1,
_id: 0
}
}, {
$sort: {
time: 1
}
}, function(err, result) {
// ...
})
I also recommend you to use raw time values (in milliseconds), because it's much easier and because it'll keep you from making a mistake. You could cast time into timeParts after $group using $project operator.