2

I'm new to JavaScript and I'm really lost here. Here is some data produced by PHP json_encode() (and limited to most pertinent keys) :

[
  {
    "product_option_id":"229",
    "product_option_value":
    [
      {
        "product_option_value_id":"21",
        "option_value_id":"51",
        "price":"1,22 €",
        "price_prefix":"+"
      },
      {
        "product_option_value_id":"22",
        "option_value_id":"52",
        "price":false,
        "price_prefix":"+"
      },
      {
        "product_option_value_id":"23",
        "option_value_id":"53",
        "price":"2,42 €",
        "price_prefix":"+"
      }
    ],
    "option_id":"14",
    "type":"radio",
    "value":""
  },
  {
    "product_option_id":"228",
    "product_option_value":
    [
      {
        "product_option_value_id":"19",
        "option_value_id":"49",
        "price":"1,22 €",
        "price_prefix":"+"
      },
      {
        "product_option_value_id":"20",
        "option_value_id":"50",
        "price":"2,42 €",
        "price_prefix":"+"
      }
    ],
    "option_id":"13",
    "type":"select",
    "value":""
  }
]

I need to access price and price_prefix values (in JavaScript) knowing product_option_id and product_option_value_id.

How do I do that ? Should I go for a loop ?

Update :

Thanks for replies. Unless I missed something, it appears that in my case arrays (as ugly as they may be…) are much more efficient than all the proposed solutions (I'll try another approach, formatting a JSON object corresponding to my needs with PHP rather than using the "default" one, but it's off topic here). Though I'm not fond of adding libraries and it's a bit slower than most other solutions, I'll accept Matt's solution because it really seems to make life easier as far as JSON access is concerned. But it should be noted that Yeldard and Barmar's (almost cloned) solutions are faster than other propositions.

Community
  • 1
  • 1
Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
  • What have you already tried? Did you try a loop? – Andy Aug 12 '15 at 09:43
  • You have to use nested loops. – Barmar Aug 12 '15 at 09:45
  • @Andy : Actually the question is more "what is the most efficient way to do that ?" — I know how to loop. I rephrased before to publish thinking it might be off topic otherwise, and lost the meaning, sorry. – Skippy le Grand Gourou Aug 12 '15 at 09:47
  • Also, I found [Access / process (nested) objects, arrays or JSON](http://stackoverflow.com/questions/11922383/access-process-nested-objects-arrays-or-json) just after posting of course. I'm reading, it, it does help (I considered deleting my question) but for now I don't see anything about access by value. – Skippy le Grand Gourou Aug 12 '15 at 09:49
  • Out of interest, what's the purpose of testing the prepocessed array values (ABC) in the perf tests? Is that the format you are going to look at creating in PHP? – Matt Aug 13 '15 at 19:50
  • @Matt : Yes, this is how my data looks like — it's a subsample of the JSON array of the question. For my purposes I finally simply replaced these arrays into simple JSON objects which are accessed directly (i.e. `var J = { "option[229]" : { "21" : 1.22, "22" : 0, "23" : 2.42 }, … }`, accessed by `J["option[229]"][21]`). – Skippy le Grand Gourou Aug 14 '15 at 19:16

7 Answers7

2

you can do like this

    for(var i in jsonData) {
    var item = jsonData[i];
    if(item.product_option_id == 229) {
        for(var j in item.product_option_value){
            var item1 = item.product_option_value[j];
            if(item1.product_option_value_id == 21) {
                //your item here
                break;
            }
        }
       break;
    }

}
Raghavendra
  • 3,530
  • 1
  • 17
  • 18
  • This reports all the prices, not just the ones he's looking for. – Barmar Aug 12 '15 at 09:47
  • 1
    You shouldn't use `for in` loops for arrays for a bunch of reasons. – Andy Aug 12 '15 at 09:49
  • IE 6 supported is discontinued don;t worry all are using latest – Raghavendra Aug 12 '15 at 09:49
  • @raghavendra, not sure what you mean by IE6, but [this still affects browsers today](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea). – Andy Aug 12 '15 at 12:23
2

This should do it:

var productOptionId = 229;
var productOptionValue = 22;

var matchingOuter = yourData.filter(function(i){
    return i.product_option_id === productOptionId; 
})[0];

if (matchingOuter) {
    var matchingInner = matchingOuter.product_option_value.filter(function(i){
        return i.product_option_value === productOptionValue;
    })[0];
}  

If a matching item exists it will be assigned to matchingInner

codebox
  • 19,927
  • 9
  • 63
  • 81
2

Use nested loops to search through the main array and the sub-arrays, looking for the matching element.

function find_product(product_option_id, product_option_value_id) {
    for (var i = 0; i < products.length; i++) {
        var product = products[i];
        if (product.product_option_id == product_option_id) {
            for (var j = 0; j < product.product_option_value.length; j++) {
                var value = product.product_option_value[j];
                if (value.product_option_value_id == product_option_value_id) {
                    return { price: value.price, price_prefix: value.price_prefix }
                }
            }
        }
    }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
2

Use filter on the main array to grab the right object, filter again on the option_value_id, then map on the returned array to get a single price/prefix object. map and filter both return arrays which is why you see the code picking up the first element ([0]) in a couple of places.

function getData(data, options) {
    return data.filter(function (product) {
      return product.product_option_id === options.id;
    })[0].product_option_value.filter(function (details) {
       return details.product_option_value_id === options.optionId;
    }).map(function(el) {
      return { price: el.price, prefix: el.price_prefix }
    })[0];
}

getData(data, { id: '229', optionId: '23' }); // { price: "2,42 €", prefix: "+" }

DEMO

Andy
  • 61,948
  • 13
  • 68
  • 95
2

Following would do:

function getProductValues(products, product_option_id, product_option_value_id) {
    if (!product_option_id || !product_option_value_id) {
        return;
    }

    return products.filter(function(product) {
        return +product.product_option_id === product_option_id;
    }).map(function (product) {
        var option_values = product.product_option_value;
        return option_values.filter(function (option) {
            return +option.option_value_id === product_option_value_id;
        })[0] || [];
    })[0] || [];
}

Usage:

getProductValues(data, 229, 51)

Result:

{product_option_value_id: "21", option_value_id: "51", price: "1,22 €", price_prefix: "+"}
gor181
  • 1,988
  • 1
  • 14
  • 12
2

lodash would make this easier and neater. It provides _.find or _.filter depending on if your id's are unique or not.

var record = _.find( data_structure, {
  "product_option_id": "229"
})
if ( !record ) throw new Error("Record not found");

var value = _.find( record.product_option_value, {
  "product_option_value_id":"22" 
})
if ( !value ) throw new Error("Value not found");

console.log( "price[%s] prefix[%s]", value.price, value.price_prefix )

Demo

For more complex data selection, you might want to look at sift.js. It's based on mongodb's query system.

var records = sift({ 
    "product_option_id": "229", 
    "product_option_value": {
      $elemMatch: {
        "product_option_value_id": "22"  
      }
    }
  },
  data_structure
)
Matt
  • 68,711
  • 7
  • 155
  • 158
  • lodash really seems to make life easier indeed. Though your solution works on jsfiddle, [I couln't make it working on jsperf](http://jsperf.com/ugly-array-vs-nested-json), any clue why ? (I get "TypeError: t.call is not a function.") – Skippy le Grand Gourou Aug 12 '15 at 14:52
  • You're using [underscore's `find`](http://underscorejs.org/#find) which requires a function for the predicate (hence `t.call`). lodash supports the underscore API but extends it to support the object notation I used. From my experience I've found lodash to be better, especially around their release/testing area. Try sourcing [lodash.js](https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js) – Matt Aug 12 '15 at 17:38
  • Thanks — I could have sweared I sourced lodash.js, can't understand how it became underscore… – Skippy le Grand Gourou Aug 12 '15 at 17:46
1

Yes, you need to enumerate through the array and find your items:

Here is the working code which outputs price_prefix and price of product with product_option_id = 228 and product_option_value_id = 19. You can replace these values with your own.

for (var i = 0; i < obj.length; i++) // Enumerate through array
{
    var item = obj[i];
    if (item.product_option_id === "228") // Filtering items by product_option_id
    {
        // When necessary product_option_id found
        for (var j = 0; j < item.product_option_value.length; j++) // Enumerate through its products
        {
            var productItem = item.product_option_value[j]; 
            if (productItem.product_option_value_id === "19") // Filtering by product_option_value_id
            {
                // here it is. productItem is found! do whatever you want with it
                alert(productItem.price_prefix + " " + productItem.price);
            }
        }
    }
}

Working JSFiddle demo.

Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
  • @raghavendra No, I did not. The OP didn't mention anything about uniquness of values. So, if value is unique, then breaks are not necessary. If value is not unique, then how could you know that OP didn't want to output them all? – Yeldar Kurmangaliyev Aug 12 '15 at 09:59
  • there are only 229 or 228 questions anyway i agree that too – Raghavendra Aug 12 '15 at 10:01