1

I would like to sort some data im receiving in the following structure by timestamp:

{
  art_AYNgc8KwfbewFawAsogtH1Eii5eu7kKfjM3CrEpFhzWY: [
    timestamp: '1613713166629',
    art_type: 'Type: image/jpeg - Size: 6442.15 Ko - Dimensions: 4000 x 6000 px',
    art_name: 'Name 1',
  ],
  art_FJPKZQKfkfzNnF6AZRiayzVdKEyCg3ZJc4Kb6hTEZMUH: [
    timestamp: '1613868065000',
    art_type: 'Type: image/png - Size: 8475.15 Ko - Dimensions: 3000 x 2000 px',
    art_name: 'Name 2',
  ],
  art_HNXph9RtNC9MT7oK2oCvVboyEUUf8bKQLvhQbpyVRbKR: [
    timestamp: '1613799567000',
    art_type: 'Type: image/jpg - Size: 3475.15 Ko - Dimensions: 1500 x 1000 px',
    art_name: 'Name 3'
  ]
}

But while keeping the exact same data structure in the result (object with key value).

I tried the following:

let sortedData = Object.values(entryData ).sort(function(a,b){
  return new Date(b.timestamp) - new Date(a.timestamp);
});

Problem is i get a different structure (array vs object):

[
  [
    timestamp: '1613713166629',
    art_type: 'Type: image/jpeg - Size: 6442.15 Ko - Dimensions: 4000 x 6000 px',
    art_name: 'Name 1',
  ],
  [
    timestamp: '1613799567000',
    art_type: 'Type: image/jpg - Size: 3475.15 Ko - Dimensions: 1500 x 1000 px',
    art_name: 'Name 3'
  ],
  [
    timestamp: '1613868065000',
    art_type: 'Type: image/png - Size: 8475.15 Ko - Dimensions: 3000 x 2000 px',
    art_name: 'Name 2',
  ]
]

Thank you in advance for the help.

Christophe
  • 145
  • 1
  • 1
  • 12

3 Answers3

2

You cannot sort / rearrange the properties inside of a JS object.

For it to have orders, you must use some kind of array, or use JS's Map object, or rebuild an object, which can maintain the insertion order.

So you need to use one of the above methods, to have order, and perhaps use it to map into your original object. Or you can also just keep all the data within the array or Map object and disregard the original data object.

  • As of ES2015 [this is no longer true](https://262.ecma-international.org/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys). – Bill Sourour Feb 22 '21 at 05:20
  • @BillSourour you mean it is also by insertion order? – eulerIdentity Feb 22 '21 at 05:20
  • Yes. The specific, relevant quote from the spec is "For each own property key P of O that is a String but is not an integer index, in property creation order. Add P as the last element of keys." – Bill Sourour Feb 22 '21 at 05:22
  • ok... my original answer said "no guaranteed order" but now changed – eulerIdentity Feb 22 '21 at 05:22
  • Your edit is correct. You cannot re-order the properties of an object, but you can create a new object and guarantee the order of the properties. That's how/why my answer works. – Bill Sourour Feb 22 '21 at 05:27
  • so I guess, "by insertion order, unless if it is numeric"... I tried and it is "all the non-negative integers first, by ascending order, and then the insertion order" – eulerIdentity Feb 22 '21 at 05:28
  • Yes. Numbers are treated differently than strings. OP's example uses strings as the keys, so it's achievable, but that's a good caveat to be aware of. – Bill Sourour Feb 22 '21 at 05:30
  • 1
    @BillSourour you have to love JS – eulerIdentity Feb 22 '21 at 05:34
2

Since ES6 objects have a travesal order. Mainly if the keys are non numeric strings they maintain the order in which they were inserted. If the keys are numeric strings ("123" for example), the are arranged in ascending order.

Convert the object to an array [key, value] entries using Object.entries(), sort the entries by take the value using destructuring. Convert back to an object with Object.fromEntries().

Note: you don't to convert the timestamp to a Date. Use unary + to convert it to a number.

const entryData = {"art_AYNgc8KwfbewFawAsogtH1Eii5eu7kKfjM3CrEpFhzWY":{"timestamp":"1613713166629","art_type":"Type: image/jpeg - Size: 6442.15 Ko - Dimensions: 4000 x 6000 px","art_name":"Name 1"},"art_FJPKZQKfkfzNnF6AZRiayzVdKEyCg3ZJc4Kb6hTEZMUH":{"timestamp":"1613868065000","art_type":"Type: image/png - Size: 8475.15 Ko - Dimensions: 3000 x 2000 px","art_name":"Name 2"},"art_HNXph9RtNC9MT7oK2oCvVboyEUUf8bKQLvhQbpyVRbKR":{"timestamp":"1613799567000","art_type":"Type: image/jpg - Size: 3475.15 Ko - Dimensions: 1500 x 1000 px","art_name":"Name 3"}};

const sortedData = Object.fromEntries(
  Object.entries(entryData)
  .sort(([, a] ,[, b]) => +b.timestamp - +a.timestamp)
);

console.log(sortedData);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Thank you very much, will study this approach to understand it better but it worls! I also have a question, why when i console.log the initial data object, i get the value for each key (art_...) with [ ... ] instead of { ... }? Since the content is structured like an object – Christophe Feb 22 '21 at 07:31
  • Welcome. I'm seeing an object in my console. Try to wrap that content with `JSON.stringify()` before logging it - `console.log(JSON.stringify(sortedData))`. – Ori Drori Feb 22 '21 at 09:22
1

Sort on the keys and reduce back down to an object. Something like:

let entryData = { art_AYNgc8KwfbewFawAsogtH1Eii5eu7kKfjM3CrEpFhzWY: {
    timestamp: '1613713166629',
    art_type:
      'Type: image/jpeg - Size: 6442.15 Ko - Dimensions: 4000 x 6000 px',
    art_name: 'Name 1',
  }, art_FJPKZQKfkfzNnF6AZRiayzVdKEyCg3ZJc4Kb6hTEZMUH: {
    timestamp: '1613868065000',
    art_type: 'Type: image/png - Size: 8475.15 Ko - Dimensions: 3000 x 2000 px',
    art_name: 'Name 2',
  }, art_HNXph9RtNC9MT7oK2oCvVboyEUUf8bKQLvhQbpyVRbKR: {
    timestamp: '1613799567000',
    art_type: 'Type: image/jpg - Size: 3475.15 Ko - Dimensions: 1500 x 1000 px',
    art_name: 'Name 3',
  },
};

Object.keys(entryData)
  .sort((a, b) => entryData[b].timestamp - entryData[a].timestamp)
  .reduce((sorted, entry) => {
    sorted[entry] = entryData[entry];
    return sorted;
  }, {});
Bill Sourour
  • 1,153
  • 1
  • 10
  • 20