Get the first day of this month from an array of objects
Asked Answered
E

3

6

 If today is 2018-11-28, I have an array of object like that:

  const dataset = [
  {
    "timestamp": "2018-11-28T16:38:07.610Z",
    "value": 751.998557581834
  },
  {
    "timestamp": "2018-11-27T16:38:07.610Z",
    "value": 644.9987628195244
  },
  {
    "timestamp": "2018-11-26T16:38:07.610Z",
    "value": 766.9985288101943
  },
  {
    "timestamp": "2018-11-25T16:38:07.610Z",
    "value": 953.9981701237627
  },
  {
    "timestamp": "2018-11-24T16:38:07.610Z",
    "value": 912.9982487662423
  },
  {
    "timestamp": "2018-11-23T16:38:07.610Z",
    "value": 402
  },
  {
    "timestamp": "2018-11-22T16:38:07.610Z",
    "value": 914.9982449300243
  },
  {
    "timestamp": "2018-11-21T16:38:07.610Z",
    "value": 769.9985230558668
  },
  {
    "timestamp": "2018-11-20T16:38:07.610Z",
    "value": 772.9985173015398
  },
  {
    "timestamp": "2018-11-19T16:38:07.610Z",
    "value": 176
  },
  {
    "timestamp": "2018-11-18T16:38:07.610Z",
    "value": 978.9981221710306
  },
  {
    "timestamp": "2018-11-17T16:38:07.611Z",
    "value": 342
  },
  {
    "timestamp": "2018-11-16T16:38:07.611Z",
    "value": 498.9990428634777
  },
  {
    "timestamp": "2018-11-15T16:38:07.611Z",
    "value": 326
  },
  {
    "timestamp": "2018-11-14T16:38:07.612Z",
    "value": 649.9987532289786
  },
  {
    "timestamp": "2018-11-13T16:38:07.612Z",
    "value": 70
  },
  {
    "timestamp": "2018-11-12T16:38:07.612Z",
    "value": 349
  },
  {
    "timestamp": "2018-11-11T16:38:07.612Z",
    "value": 191
  },
  {
    "timestamp": "2018-11-10T16:38:07.612Z",
    "value": 154
  },
  {
    "timestamp": "2018-11-09T16:38:07.613Z",
    "value": 109
  },
  {
    "timestamp": "2018-11-08T16:38:07.613Z",
    "value": 237
  },
  {
    "timestamp": "2018-11-07T16:38:07.613Z",
    "value": 398
  },
  {
    "timestamp": "2018-11-06T16:38:07.613Z",
    "value": 606.9988357076774
  },
  {
    "timestamp": "2018-11-05T16:38:07.614Z",
    "value": 131
  },
  {
    "timestamp": "2018-11-04T16:38:07.614Z",
    "value": 397
  },
  {
    "timestamp": "2018-11-03T16:38:07.614Z",
    "value": 583.9988798241893
  },
  {
    "timestamp": "2018-11-02T16:38:07.614Z",
    "value": 362
  },
  {
    "timestamp": "2018-11-01T16:38:07.614Z",
    "value": 686.998682258936
  },
  {
    "timestamp": "2018-10-31T16:38:07.614Z",
    "value": 131
  },
  {
    "timestamp": "2018-10-30T16:38:07.614Z",
    "value": 212
  }
]

The objects are created using this code:

  import { DateTime } from 'luxon'

  const timestamp = startDate.minus({ days: i }).toJSDate()
  return { timestamp: timestamp, value: randomValue }

I want the object containing the first day of this month so, in this example, I want:

  {
    "timestamp": "2018-11-01T16:38:07.614Z",
    "value": 686.998682258936
  }

This is what I tried:

const date = new Date()
const firstDayOfThisMonth = new Date(date.getFullYear(), date.getMonth(), 1)
const firstDayOfThisMonthSub = firstDayOfThisMonth.toString().substring(0, 15)
const bo = dataset.map((d, i) => {
  const sub = d.toString().substring(0, 15)
  if (sub === firstDayOfThisMonthSub) return d
})

It doesn't work (I get an array of undefined) and I hope there is a smarter way to do that. I can use Javascript Date object or Luxon library.

Thanks

Er answered 28/11, 2018 at 18:20 Comment(2)
What is dataset variable?Magical
@Magical dataset is the array of objects. I update my main questionEr
M
12

With luxon :

const firstDayOfThisMonth = DateTime.local().startOf('month')
const firstDayRecord = dataset.find(record => {
 return DateTime.fromISO(record.timestamp).hasSame(firstDayOfThisMonth, 'day')
})

This should do the trick

Magical answered 28/11, 2018 at 18:35 Comment(2)
I updated snippet, there were some errors, i'm not familiar with luxon sryMagical
It's a luxon thing, i never used the library but the logic is here.Magical
M
1

You have UTC timestamps but it's not clear to me if you want the local first day of the month or UTC first day of the month. If you want UTC, getting the first day of the month as an ISO 8601 date can be simplified to:

let date = new Date().toISOString().slice(0,8) + '01';

Noting that the UTC month will be different to the local month for the period of the host timezone offset at the start or end of the month depending on whether it's east or west of Greenwich respectively. You can then use filter to get matching elements, E.g.

var data = 
  [{"timestamp": "2018-11-02T16:38:07.614Z","value": 362},
   {"timestamp": "2018-11-01T16:38:07.614Z","value": 686.998},
   {"timestamp": "2018-10-31T16:38:07.614Z","value": 131},
   {"timestamp": "2018-10-30T16:38:07.614Z","value": 212}];
   
var s = new Date().toISOString().slice(0,8) + '01';
var d = data.filter(o => o.timestamp.slice(0,10) == s);
console.log(d);

However, if you want to find the timestamp for the local first day of the month, you should convert the timestamps to Dates and compare with the start and end of the local first day of the month, e.g.

var data = 
  [{"timestamp": "2018-11-02T16:38:07.614Z","value": 362},
   {"timestamp": "2018-11-01T16:38:07.614Z","value": 686.998},
   {"timestamp": "2018-10-31T16:38:07.614Z","value": 131},
   {"timestamp": "2018-10-30T16:38:07.614Z","value": 212}];
    
var d = new Date();
d.setDate(1);
let firstOfMonthStart = d.setHours(0,0,0,0);
let firstOfMonthEnd   = d.setHours(23,59,59,999);
let t = data.filter(o => {
  let d = new Date(o.timestamp);
  return d >= firstOfMonthStart && d <= firstOfMonthEnd;
});
console.log(t);

Note that firstOfMonthStart and firstOfMonthEnd will be time values, not Dates, but the comparison works because < and > coerce the values to number, so the comparison works exactly as if they were Dates.

For someone whose local timezone is, say, +10:00, the returned array in November 2018 is:

[{timestamp: "2018-10-31T16:38:07.614Z", value: 131}]

since their local start of month is 2018-10-31T14:00:00Z.

Margarettemargarida answered 28/11, 2018 at 20:52 Comment(0)
O
0

map() isn't the right tool. It will give you a value for every item in the original array, even if it's undefined. To get a subset of an array, use filter().

Since these are nice ISO 8601 date strings. You can build a datestring like '2018-11-01' and filter based on wether the dat starts with it:

let a = [  {"timestamp": "2018-11-28T16:38:07.610Z","value": 751.998557581834},{"timestamp": "2018-11-27T16:38:07.610Z","value": 644.9987628195244},{"timestamp": "2018-11-26T16:38:07.610Z","value": 766.9985288101943},{"timestamp": "2018-11-25T16:38:07.610Z","value": 953.9981701237627},{"timestamp": "2018-11-24T16:38:07.610Z","value": 912.9982487662423},{"timestamp": "2018-11-23T16:38:07.610Z","value": 402},{"timestamp": "2018-11-22T16:38:07.610Z","value": 914.9982449300243},{"timestamp": "2018-11-21T16:38:07.610Z","value": 769.9985230558668},{"timestamp": "2018-11-20T16:38:07.610Z","value": 772.9985173015398},{"timestamp": "2018-11-19T16:38:07.610Z","value": 176},{"timestamp": "2018-11-18T16:38:07.610Z","value": 978.9981221710306},{"timestamp": "2018-11-17T16:38:07.611Z","value": 342},{"timestamp": "2018-11-16T16:38:07.611Z","value": 498.9990428634777},{"timestamp": "2018-11-15T16:38:07.611Z","value": 326},{"timestamp": "2018-11-14T16:38:07.612Z","value": 649.9987532289786},{"timestamp": "2018-11-13T16:38:07.612Z","value": 70},{"timestamp": "2018-11-12T16:38:07.612Z","value": 349},{"timestamp": "2018-11-11T16:38:07.612Z","value": 191},{"timestamp": "2018-11-10T16:38:07.612Z","value": 154},{"timestamp": "2018-11-09T16:38:07.613Z","value": 109},{"timestamp": "2018-11-08T16:38:07.613Z","value": 237},{"timestamp": "2018-11-07T16:38:07.613Z","value": 398},{"timestamp": "2018-11-06T16:38:07.613Z","value": 606.9988357076774},{"timestamp": "2018-11-05T16:38:07.614Z","value": 131},{"timestamp": "2018-11-04T16:38:07.614Z","value": 397},{"timestamp": "2018-11-03T16:38:07.614Z","value": 583.9988798241893},{"timestamp": "2018-11-02T16:38:07.614Z","value": 362},{"timestamp": "2018-11-01T16:38:07.614Z","value": 686.998682258936},{"timestamp": "2018-10-31T16:38:07.614Z","value": 131},{"timestamp": "2018-10-30T16:38:07.614Z","value": 212}]

let now =  new Date()
 // start of month is always 01
 // month is zero indexed
let pattern = `${now.getUTCFullYear()}-${now.getUTCMonth()+1}-01`   
let filtered = a.filter(item => item.timestamp.startsWith(pattern))
console.log(filtered)
Orangutan answered 28/11, 2018 at 18:28 Comment(3)
Here you're comparing a string based on the local start of month with strings for the UTC start of month, ignoring the timezone offset. That can be a difference of +14 to -12 hours.Margarettemargarida
You're right, Thanks @Margarettemargarida I overlooked the Z. I fixed it (I think — I'm not sure how trustworthy gets UTC methods are across browsers).Orangutan
The methods are bullet proof, they've been there since the very beginning. It's just not clear to me whether the OP wants UTC or local.Margarettemargarida

© 2022 - 2024 — McMap. All rights reserved.