6

I have an object array as follows:

products = [
  {
    id: 1,
    title: "Product 1",
    specifications: {
      price: 1.55,
      discount: 15,
      attributes: [
        {
          l1: 100,
          l2: 80
          height:200,
          weight: 15,
          parameters: [
            {
              id: 199199 // this is how I identify the parameter
              size: 185 // this is what I want to change
            }, ... 
          ]    
        }, ...
      ]

    }
  }, ...
]

... and an array of changes to parameters I want to apply, for example: change size to 189 where product.specifications.attributes.parameters.id == 199199.

I'd like to do this without flattening any elements as they are part of a Vue.js data structure, it will break the reactivity.

How could I do this? I am open to using Underscore or lo-dash

Nick M
  • 2,424
  • 5
  • 34
  • 57

3 Answers3

3

This looks ugly, but it is effective:

To make it more dynamic, let's use variables: identifier will be your '199199' value and new_size for the '189' value.

methods: {
    updateRecord: function(identifier, new_size) {
        this.products.map(function(product) {   
            product.specifications.attributes.map(function(attribute)           {
                attribute.parameters.map(function(parameter) {
                    if (parameter.id == identifier) parameter.size = new_size;
                })
            });
        });
    }
}

Here is a working fiddle: https://jsfiddle.net/crabbly/eL7et9e8/

crabbly
  • 5,106
  • 1
  • 23
  • 46
  • Very nice. Since the parameter ids are unique, should I break after changing size? As per http://stackoverflow.com/questions/183161/best-way-to-break-from-nested-loops-in-javascript – Nick M Nov 22 '16 at 22:56
  • Ran a test with some 5000 products on screen and I chose your answer because it seems it takes some few ms less to update all the data I have, compared to the one submitted by stasovlas. Will try to break cycles after updates, this will save a lot more cycles, as a product only gets shown once. – Nick M Nov 23 '16 at 16:24
  • @NickM Unfortunately there isn't a built in break option for Array.prototype.map. Unless you have an extreme large number of items, you may not see much difference in performance. – crabbly Nov 23 '16 at 17:02
1
_.forEach(products, function(product) {
    _.forEach(_.get(product, 'specifications.attributes', []), function(attribute) {
        _.set(_.find(attribute.parameters, {id: 199199}), 'size', 189);
    });
});
stasovlas
  • 7,136
  • 2
  • 28
  • 29
  • Will it not throw if a product does not have the "attributes" key? – Nick M Nov 21 '16 at 05:01
  • Also, .map produces a new array, which is not what I want...could this be rewritten with a chain of .selects and an .extend({ size: 189 }) or something along those lines? to edit the value "in place" – Nick M Nov 21 '16 at 05:07
0

I believe what you want is underscore's findIndex() - http://underscorejs.org/#findIndex. Once you find which element in the array you want to apply the changes to (comparing the nested id to what you are looking for) you can then make the change to that particular element.

Kalman
  • 8,001
  • 1
  • 27
  • 45