michalsnik / vue-computed-helpers

Computed helpers for Vue apps

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[enhancement] Computed hashmap when given unique prop in array of objs

alexsasharegan opened this issue · comments

I have made extensive use of computing a hash map (plain js obj) by a unique property in an array of objects. This avoid needless iteration of array structures when using things like Array.prototype.find.

More simply, let's say you fetch an array of products from the server and place them in state (Vuex/component data). You may have orders that reference just the id off the product. Ideally you could get the product by ID without always looping through the array of products.

I'll see if I can PR my personal solution in the next couple days.

Hey @alexsasharegan! Thanks for posting this issue.

Do you mean something like this?

export default {
  data() {
    return {
      someArray: [
        { id: 1, name: 'lorem' },
        { id: 2, name: 'ipsum' },
        { id: 3, name: 'dolor' }
      ]
    }
  },
  computed: {
    someItem: findBy('someArray', 'id', 2)
  }
}

If yes - I'm working on this now so it'll be available today (find and findBy), currently there is only filter and filterBy that you could use alternatively. If not let me know what you mean exactly :)

Sorry, I meant to include some background. Here's an example case:

Computed Hash Map

export function reduceToObjByProp(prop) {
  return function objReducer(memo, model) {
    return (memo[model[prop]] = model), memo
  }
}

new Store({
  state: {
    products: [
      { id: 1, name: 'lorem' },
      { id: 2, name: 'ipsum' },
      { id: 3, name: 'dolor' }
    ]
  },
  getters: {  
    productsById({ products }) {
      return products.reduce(reduceToObjByProp("id"), {})
    }
  }
})

I like this approach over Array.prototype.find because you get instant access by any id (or other prop).

Array.prototype.find

export default {
  data() {
    return {
      someArray: [
        { id: 1, name: 'lorem' },
        { id: 2, name: 'ipsum' },
        { id: 3, name: 'dolor' }
      ]
    }
  },
  computed: {
    byId: () => id => this.$store.state.products.find(p => p.id === id)
  }
}

@alexsasharegan In case of storing such data in the store I think the best way is to just use normalizr

But if somehow you store data in array, is this what you'd like to be able to do in a component?

export default {
  computed: {
    productsById: toObjBy('arrayProp', 'id')
  }
}

I get a lot of use out of this say when rendering shopping cart items out. The store has an array of product objects, but the cart item just has an id reference. I don't need normalizr, I just want the fastest, lightest way to pluck product where { id: x }. Vuex docs show that getter pattern where it returns a function that does a find iteration, but that can loop more than it needs to. If products never get reloaded, that computed map example will stay put and all reads are simple and fast. It doesn't add much in the way of memory either because every product is just a reference to the original object.

Yes, I think that's exactly what I'd be looking for.

Hmm, and what's wrong in keeping that getter in vuex? It seems like a best place for such a scenario to me. I don't see a clear point of moving it to a computed helper. On one hand I want those helpers to actually save time and burden, but on the other I don't want them to introduce too much of a logic that should actually be kept in other place of the application. Otherwise pleople might start to create too complex components, which is not a good thing.

I see however 5 thumbs already so I might give it another thought :)

I would only use it in vuex! But if I'm including computed helpers, can't I import it and use it in vuex? Then we all get community tested code!

Currently it only works in components, because of the context. In vuex getter this doesn't relate to the state. So it would need a bit of tweaking to work in both cases. But that might be a good step forward actually.

Ah, gotcha. I starred this from a twitter post, but haven't started another project yet where I can play with it. Maybe you could take the lodash approach and have all the computed helpers have base functions, then just wrap them for instance helpers/store helpers?

Not sure about this regarding how some of the computed helpers work, but I'll be thinking of the possible elegant ways of providing those helpers both for controllers and vuex.

Currently if you want to use those in vuex getters you have to bind the proper context like this:

getters: {
  currentLaunch (state) {
    return findBy('all', 'id', state.currentLaunchId).bind(state)()
  }
}

But I'm not sure whether this will work properly when state changes.