oderfla
oderfla

Reputation: 1797

How to sort object according to nested array elements

I need to sort the main data by the oldest created account. In the example below, the first element in the list would be id = 2. That's because the id = 2 contains the oldest created account (named account3, which was created at 2020-10-05, while the other accounts have been created after that date). I'm using nodejs. Is there an es-function that can solve this problem in an easy way?

The object data looks like this:

{
  "data": [{
    "id": 1,
    "accounts": [{
      "id": 333,
      "data": {
        "name": "account1",
        "createdAt": "2020-10-07T09:27:28.032Z"
      }
    }]
  }, {
    "id": 2,
    "accounts": [{
      "id": 334,
      "data": {
        "name": "account2",
        "createdAt": "2020-10-06T09:27:28.032Z"
      }
    }, {
      "id": 335,
      "data": {
        "name": "account3",
        "createdAt": "2020-10-05T09:27:28.032Z"
      }
    }]
  }]
}

Upvotes: 0

Views: 58

Answers (2)

secan
secan

Reputation: 2679

const data = [{
  "id": 1,
  "accounts": [{
    "id": 333,
    "data": {
      "name": "account1",
      "createdAt": "2020-10-07T09:27:28.032Z"
    }
  }]
}, {
  "id": 2,
  "accounts": [{
    "id": 334,
    "data": {
      "name": "account2",
      "createdAt": "2020-10-06T09:27:28.032Z"
    }
  }, {
    "id": 335,
    "data": {
      "name": "account3",
      "createdAt": "2020-10-05T09:27:28.032Z"
    }
  }]
}]

const sorted = data.sort((a, b) => {
  const aOldestDate = a.accounts.reduce((acc, item) => {
    const itemDate = new Date(item.data.createdAt);
    return itemDate < acc && itemDate || acc;
  }, new Date());
  
  const bOldestDate = b.accounts.reduce((acc, item) => {
    const itemDate = new Date(item.data.createdAt);
    return itemDate < acc && itemDate || acc;
  }, new Date());
  
  return aOldestDate - bOldestDate;
});

console.log(sorted);

Upvotes: 1

plalx
plalx

Reputation: 43718

You can often solve this problem with map --> sort --> map. It does 3 passes on the input but remains O(n log n). You could further optimize, but I doubt this becomes a bottleneck.

  1. Map to [record, oldestAccountDate] tuples.

  2. Sort the tuples by oldestAccountDate.

  3. Map again to unwrap the record.

const wrapper = {
  "data": [{
    "id": 1,
    "accounts": [{
      "id": 333,
      "data": {
        "name": "account1",
        "createdAt": "2020-10-07T09:27:28.032Z"
      }
    }]
  }, {
    "id": 2,
    "accounts": [{
      "id": 334,
      "data": {
        "name": "account2",
        "createdAt": "2020-10-06T09:27:28.032Z"
      }
    }, {
      "id": 335,
      "data": {
        "name": "account3",
        "createdAt": "2020-10-05T09:27:28.032Z"
      }
    }]
  }]
};

wrapper.data = wrapper.data
  .map(rec => [rec, Math.min(...rec.accounts.map(acc => new Date(acc.data.createdAt)))])
  .sort((a, b) => a[1] - b[1])
  .map(tuple => tuple[0]);
  
  
 console.log(wrapper);

Upvotes: 1

Related Questions