1

I have been breaking my head the last few days trying to implement the following functionality:

I have an incoming object, with a nested sub-object that's a list of dates. I would like to filter the original object against a range of dates, and return the modified filtered object, with only the specified dates.

Here is the desired functionality:

let incomingObject = {
    'one': {
        id: "one",
        dates: {
            "2021-05-01": [{ a: "foo", b: "bar" }],
            "2021-05-02": [{ a: "foo", b: "bar" }] } },
    'two': {
        id: "two",
        dates: {
            "2021-05-01": [{ a: "foo", b: "bar" }, { a: "foo2", b: "bar2" }],
            "2021-05-02": [{ a: "baz", b: "far" }] } },
    'three': {
        id: "three",
        dates: {
            "2021-05-03": [{ a: "foo", b: "bar" }],
            "2021-05-02": [{ a: "foo", b: "bar" }] } } };

// Function to get an array between two dates
const getDaysArray = function (s, e) {
    for (var a = [], d = new Date(s); d <= new Date(e); d.setDate(d.getDate() + 1)) {
        a.push(new Date(d));
    }
    let aStrings = a.map((date) => date.toISOString().slice(0, 10));
    return aStrings;
};

I have no idea how to implement this function, that would return the "filtered" object:

filterResults(incomingObject, getDaysArray("2021-05-01", "2021-05-01"));

This is the desired result - all the (sub-)objects that don't pass the filter, are left out:

let desiredResult = {
    'one': {
        id: "one",
        dates: {
            "2021-05-01": [{ a: "foo", b: "bar" }] } },
    'two': {
        id: "two",
        dates: {
            "2021-05-01": [{ a: "foo", b: "bar" }, { a: "foo2", b: "bar2" }] } } };

And my progress so far:

let dateRange = getDaysArray("2021-05-01", "2021-05-01");

// This logs out only the required keys –– however, logging out the whole "parent" object is completely beyond my comprehension at the moment...
const filteredImages = Object.keys(images).forEach((key) => {
    let imgObject = images[key];
    Object.keys(images[key].dates).forEach((key) => {
        if (dateRange.includes(key)) {
            console.log(key);
        }
    });
});

All help or pointers much appreciated!

Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
DmnkVD
  • 75
  • 7

3 Answers3

1

const incomingObject={one:{id:"one",dates:{"2021-05-01":[{a:"foo",b:"bar"}],"2021-05-02":[{a:"foo",b:"bar"}]}},two:{id:"two",dates:{"2021-05-01":[{a:"foo",b:"bar"},{a:"foo2",b:"bar2"}],"2021-05-02":[{a:"baz",b:"far"}]}},three:{id:"three",dates:{"2021-05-03":[{a:"foo",b:"bar"}],"2021-05-02":[{a:"foo",b:"bar"}]}}};
const getDaysArray = function(e,t){for(var a=[],n=new Date(e);n<=new Date(t);n.setDate(n.getDate()+1))a.push(new Date(n));return a.map(e=>e.toISOString().slice(0,10))}

const filterResults = (data, days) => Object.entries(data).reduce((result, [k, v]) => {
  const {dates, ...rest} = v
  const filteredDates = days.reduce((acc, date) => {
    if(v.dates[date]) acc[date] = v.dates[date]
    return acc
  }, {})
  if (Object.keys(filteredDates).length) 
    result.push([k, { ...rest, dates: filteredDates }])
  return result
}, [])

const res = filterResults(
  incomingObject,
  getDaysArray("2021-05-01", "2021-05-01")
)

console.log(Object.fromEntries(res))
.as-console-wrapper { max-height: 100% !important; top: 0; }
ulou
  • 5,542
  • 5
  • 37
  • 47
1

What about this one? I find it very concise and modern:

const filterResults = (data, days) => 
  Object.entries(data).reduce((acc, [n, { id, dates }]) => {
    const ds = Object.entries(dates).filter(([d,]) => days.includes(d))
    return ds.length ? { ...acc, [n]: { id, dates: Object.fromEntries(ds) } } : acc
  }, {})

The nice thing here is to use nested destructuring patterns and spread operator to avoid boilerplate code.

Also, while doing reduce over Object.entries, we can construct an object directly, without need of an intermediary array. In this implementation, everything is done in just one iteration over the original data array, so it performs better.


Additional information on the techniques used in the snippet:

Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
  • This is a great solution, thank you so much for sharing it! it is beyond my current comprehension –– I would like to understand it -- if you would have the time to share a commented-version of the steps, that would be very benefitial for my learning... – DmnkVD May 27 '21 at 13:02
  • @DmnkVD, Added useful links on every technique used in this snippet. – Rodrigo Rodrigues May 31 '21 at 00:02
0

You can use the array methods filter()

In your case it could look something like this:

const incoming = [
  { 
    nested: {
      date: '2021-05-01'
    }
  },
  { 
    nested: {
      date: '2021-03-16'
    }
  },
  { 
    nested: {
      date: '2021-02-05'
    }
  },
  { 
    nested: {
      date: '2021-01-20'
    }
  },
]

let someRange = ['2021-02-05', '2021-03-24']

const result = incoming.filter((item) => {
  return item.nested.date >= someRange[0] && item.nested.date <= someRange[1]
})

console.log(result)

You need to transform your object into an array with Object.values(yourArray) to use the filter() method.

EDIT:

Just read your post again. You can use the Object.keys() to retrieve the keys from your object like so:

const dates = {
  "2021-05-01": [{
      a: "foo",
      b: "bar",
    },
    {
      a: "foo2",
      b: "bar2",
    },
  ],
  "2021-05-02": [{
    a: "baz",
    b: "far",
  }, ],
}

const dateKeys = Object.keys(dates)

console.log(`Keys from date object: ${dateKeys}`)

const result = dateKeys.filter((item) => {
  return item >= '2021-05-02'
})

console.log(result)
idkwhatsgoingon
  • 658
  • 4
  • 22
  • Thank you very much for your answer! – the only problem in my case is that my keys are named as dates, e.g. `"2021-05-02": {}`, rather than `"date": "2021-05-02"`... – DmnkVD May 24 '21 at 18:07