0

I have a large VueJS app with a ton of data. Different views perform various operations on arrays of objects. I've created basic lookup arrays to find object.id in the various object arrays. What I'd like to do now is create a sorted array of object ids according to a field or fields. For example, updated_at. I think I'm overcomplicating this. I was hoping to have something like this:

let indexedByUpdatedAt = {}
objects.sort((a,b) => new Date(a.updated_at) - new Date(b.updated_at))
   .forEach((object) => { 
       indexedByUpdatedAt[object.updated_at] = object.id
})  

When new data comes in, I would have to update the array, but new data 99% of the time could just be pushed or shifted onto the array so I wouldn't have to re-index everything. Even in the case of inserting in the middle of the array, I could traverse up to a value rather than completely rechecking the array. Bad idea right?


Stephane
  • 1,613
  • 5
  • 20
  • 40
  • 1
    You could just assign `objects` to some variable somewhere and only sort once the insertions hit a specific complexity. It isn't super difficult but it does seem like over engeering unless its performance critical and its been profiled. That being said, if you wanted to go down the route, you could create a class that manages the collection for you so its a little more opaque to consumers. – Brenden Sep 04 '20 at 16:32
  • Thanks! I'm trying to reduce the amount of duplicate data in the browser. So smaller indexes seem better. Can you tell me more about the class idea? I'm already using modules in Vuex and storing things there, but I think you're suggesting something like Class.SortBy('whatever') which then checks if an index exists otherwise just sorts? – Stephane Sep 04 '20 at 16:59
  • 1
    yeah, more or less. Since any mutation to the index has to be done with the heap, you could save yourself some headaches by just wrapping that stuff up in an object. Something like [this](https://jsfiddle.net/dzkqfgvj/). – Brenden Sep 04 '20 at 17:57
  • Really cool. I'm pretty weak in TS, but are you using a pointer? I'm assuming that's what *[Symbol.iterator]() is? Or is this accessible some base level thing in TS to make this iterable? – Stephane Sep 04 '20 at 19:41
  • 1
    Thats a generator syntax, It's part of JavaScript. It makes the class iterable so you can use your normal iteration features such as spread, for of, and any function/utils that can deal with iterables such as lodash. – Brenden Sep 04 '20 at 19:52

1 Answers1

1

I don't think I grok the full picture, but it seems like you might be trying to rely on the sorted-order of an object's keys, but objects are generally sorted by their insertion order (with caveats, because JavaScript).

What you might try instead is keeping a store of your records by ID, and a separate array of the record IDs, sorted as desired.

const store = records.reduce((data, record) => {
  data[record.id] = record
  return data
}, {})

const collation = records.sort(…).map(record => record.id)

// look up a single record
store[someId]

// get all records, sorted
collation.map(id => store[id])

// get first 3 records by sort order
collation.slice(0, 3).map(id => store[id])

// add a new record at index 42
store[newRecord.id] = newRecord
collation.splice(42, 0, newRecord.id)
coreyward
  • 77,547
  • 20
  • 137
  • 166
  • yes, this is exactly what I'm already doing with different syntax. I like your naming though. When inserting into collation, I think I would run down through the collation until I find where the entry should be inserted and splice it in there. Otherwise, our code would be virtually identical I think. – Stephane Sep 04 '20 at 17:02
  • Whoa, I just read up on Maps. TYVM for that caveat link. So seems like using Object is just default but when is it preferable? Also, Maps can have objects as keys?!?! This is amazing. This may be part of my golden ticket... – Stephane Sep 04 '20 at 19:44