【问题标题】:Access by value on nested JSON arrays按值访问嵌套 JSON 数组
【发布时间】:2015-08-13 02:12:04
【问题描述】:

我是 JavaScript 新手,我真的迷路了。以下是 PHP json_encode() 生成的一些数据(仅限于大多数相关键):

[
  {
    "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":""
  }
]

知道product_option_idproduct_option_value_id,我需要访问priceprice_prefix 值(在JavaScript 中)。

我该怎么做?我应该循环吗?

更新:

感谢您的回复。除非我遗漏了什么,否则in my case 数组(尽管它们可能很丑……)似乎比所有建议的解决方案都更 much more efficient(我将尝试另一种方法,使用 PHP 而不是根据我的需要格式化 JSON 对象使用“默认”的,但这里不是主题)。虽然我不喜欢添加库并且它比大多数其他解决方案慢一点,但我会接受 Matt 的解决方案,因为就 JSON 访问而言,它似乎真的让生活更轻松。但需要注意的是,Yeldard 和 Barmar 的(几乎是克隆的)解决方案比其他命题更快。

【问题讨论】:

  • 你已经尝试过什么?你试过循环吗?
  • 你必须使用嵌套循环。
  • @Andy :实际上问题更多的是“最有效的方法是什么?” — 我知道如何循环。我之前改写了发表,认为否则它可能会偏离主题,并且失去了意义,抱歉。
  • 另外,我在发帖后就找到了Access / process (nested) objects, arrays or JSON。我正在阅读它,它确实有帮助(我考虑删除我的问题)但现在我没有看到任何关于按值访问的内容。
  • 出于兴趣,在 perf 测试中测试预处理数组值 (ABC) 的目的是什么?那是您打算在 PHP 中创建的格式吗?

标签: javascript json key-value


【解决方案1】:

lodash 将使这更容易和更整洁。它提供_.find_.filter,具体取决于您的ID 是否唯一。

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

对于更复杂的数据选择,您可能需要查看sift.js。它基于mongodb's query system

var records = sift({ 
    "product_option_id": "229", 
    "product_option_value": {
      $elemMatch: {
        "product_option_value_id": "22"  
      }
    }
  },
  data_structure
)

【讨论】:

  • lodash 似乎确实让生活更轻松。尽管您的解决方案适用于 jsfiddle,I couln't make it working on jsperf,但有什么线索吗? (我得到“TypeError:t.call 不是函数。”)
  • 您正在使用underscore's find,它需要谓词的函数(因此t.call)。 lodash 支持下划线 API,但对其进行了扩展以支持我使用的对象表示法。根据我的经验,我发现 lodash 更好,尤其是在他们的发布/测试区域。尝试采购lodash.js
  • 谢谢 — 我可以发誓我找到了 lodash.js,不明白它是如何变成下划线的……
【解决方案2】:

你可以这样做

    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;
    }

}

【讨论】:

  • 这会报告所有价格,而不仅仅是他正在寻找的价格。
  • 你不应该使用 for in 循环用于数组,原因有很多。
  • 已停止支持 IE 6 不用担心,所有用户都在使用最新版本
  • @raghavendra,不知道你说的 IE6 是什么意思,但是this still affects browsers today
【解决方案3】:

应该这样做:

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];
}  

如果存在匹配项,则将其分配给matchingInner

【讨论】:

    【解决方案4】:

    以下会做:

    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] || [];
    }
    

    用法:

    getProductValues(data, 229, 51)
    

    结果:

    {product_option_value_id: "21", option_value_id: "51", price: "1,22 €", price_prefix: "+"}
    

    【讨论】:

      【解决方案5】:

      在主数组上使用filter 来获取正确的对象,在option_value_id 上再次过滤,然后在返回的数组上使用map 以获得单个价格/前缀对象。 mapfilter 都返回数组,这就是为什么您会在几个地方看到代码拾取第一个元素 ([0])。

      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

      【讨论】:

        【解决方案6】:

        使用嵌套循环搜索主数组和子数组,寻找匹配的元素。

        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 }
                        }
                    }
                }
            }
        }
        

        【讨论】:

        • if 测试中使用“==”比“=”更好。 ;)
        【解决方案7】:

        是的,您需要遍历数组并找到您的项目:

        这是输出 product_option_id = 228 和 product_option_value_id = 19 的产品的 price_prefixprice 的工作代码。您可以用自己的值替换这些值。

        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.

        【讨论】:

        • @raghavendra 不,我没有。 OP 没有提到任何关于价值观的唯一性。因此,如果值是唯一的,则不需要中断。如果 value 不是唯一的,那么你怎么知道 OP 不想全部输出?
        • 反正只有 229 或 228 个问题我也同意
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-12
        • 2019-12-05
        • 1970-01-01
        • 2019-01-22
        • 2018-08-16
        • 1970-01-01
        相关资源
        最近更新 更多