【问题标题】:JSON find in JavaScript在 JavaScript 中查找 JSON
【发布时间】:2010-12-29 02:30:49
【问题描述】:

除了循环查找JSON 中的数据,还有更好的方法吗?用于编辑和删除。

for(var k in objJsonResp) {
  if (objJsonResp[k].txtId == id) {
    if (action == 'delete') {
      objJsonResp.splice(k,1);
    } else {
      objJsonResp[k] = newVal;
    }
    break;
  }
}

数据以地图列表的形式排列。 喜欢:

[
  {id:value, pId:value, cId:value,...},
  {id:value, pId:value, cId:value,...},
  ...
]

【问题讨论】:

  • 有人注意到删除不会改变长度并且项目@deleted位置可能保持未定义(不执行重新索引),除非它是最后一个元素,减少长度以纠正删除最后一个元素? Splice 似乎更新了它们。

标签: javascript json search


【解决方案1】:

一般解决方案

我们使用object-scan 进行大量数据处理。它有一些不错的属性,尤其是在删除安全顺序中遍历。以下是如何为您的问题实现查找、删除和替换。

// const objectScan = require('object-scan');

const tool = (() => {
  const scanner = objectScan(['[*]'], {
    abort: true,
    rtn: 'bool',
    filterFn: ({
      value, parent, property, context
    }) => {
      if (value.id === context.id) {
        context.fn({ value, parent, property });
        return true;
      }
      return false;
    }
  });
  return {
    add: (data, id, obj) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property + 1, 0, obj) }),
    del: (data, id) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property, 1) }),
    mod: (data, id, prop, v = undefined) => scanner(data, {
      id,
      fn: ({ value }) => {
        if (value !== undefined) {
          value[prop] = v;
        } else {
          delete value[prop];
        }
      }
    })
  };
})();

// -------------------------------

const data = [ { id: 'one', pId: 'foo1', cId: 'bar1' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ];
const toAdd = { id: 'two', pId: 'foo2', cId: 'bar2' };

const exec = (fn) => {
  console.log('---------------');
  console.log(fn.toString());
  console.log(fn());
  console.log(data);
};

exec(() => tool.add(data, 'one', toAdd));
exec(() => tool.mod(data, 'one', 'pId', 'zzz'));
exec(() => tool.mod(data, 'one', 'other', 'test'));
exec(() => tool.mod(data, 'one', 'gone', 'delete me'));
exec(() => tool.mod(data, 'one', 'gone'));
exec(() => tool.del(data, 'three'));

// => ---------------
// => () => tool.add(data, 'one', toAdd)
// => true
// => [ { id: 'one', pId: 'foo1', cId: 'bar1' }, { id: 'two', pId: 'foo2', cId: 'bar2' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ]
// => ---------------
// => () => tool.mod(data, 'one', 'pId', 'zzz')
// => true
// => [ { id: 'one', pId: 'zzz', cId: 'bar1' }, { id: 'two', pId: 'foo2', cId: 'bar2' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ]
// => ---------------
// => () => tool.mod(data, 'one', 'other', 'test')
// => true
// => [ { id: 'one', pId: 'zzz', cId: 'bar1', other: 'test' }, { id: 'two', pId: 'foo2', cId: 'bar2' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ]
// => ---------------
// => () => tool.mod(data, 'one', 'gone', 'delete me')
// => true
// => [ { id: 'one', pId: 'zzz', cId: 'bar1', other: 'test', gone: 'delete me' }, { id: 'two', pId: 'foo2', cId: 'bar2' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ]
// => ---------------
// => () => tool.mod(data, 'one', 'gone')
// => true
// => [ { id: 'one', pId: 'zzz', cId: 'bar1', other: 'test', gone: undefined }, { id: 'two', pId: 'foo2', cId: 'bar2' }, { id: 'three', pId: 'foo3', cId: 'bar3' } ]
// => ---------------
// => () => tool.del(data, 'three')
// => true
// => [ { id: 'one', pId: 'zzz', cId: 'bar1', other: 'test', gone: undefined }, { id: 'two', pId: 'foo2', cId: 'bar2' } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.7.1"></script>

免责声明:我是object-scan的作者

【讨论】:

    【解决方案2】:

    好的。所以,我知道这是一个旧帖子,但也许这可以帮助其他人。这不是向后兼容的,但这几乎是无关紧要的,因为 Internet Explorer 正在变得多余。

    最简单的方法来做你想要的:

    function findInJson(objJsonResp, key, value, aType){        
         if(aType=="edit"){
            return objJsonResp.find(x=> x[key] == value);
         }else{//delete
             var a =objJsonResp.find(x=> x[key] == value);
             objJsonResp.splice(objJsonResp.indexOf(a),1);
         }
    }
    

    如果您提供“编辑”作为类型,它将返回您要编辑的项目。提供其他任何东西,或者什么都不提供,它假定删除。如果您愿意,可以翻转条件。

    【讨论】:

      【解决方案3】:

      如果您在应用程序中的多个位置执行此操作,则使用客户端 JSON 数据库是有意义的,因为创建自定义搜索函数很麻烦,并且比其他方法更难维护。

      查看 ForerunnerDB,它为您提供了一个非常强大的客户端 JSON 数据库系统,并包含一种非常简单的查询语言来帮助您完成您正在寻找的事情:

      // Create a new instance of ForerunnerDB and then ask for a database
      var fdb = new ForerunnerDB(),
          db = fdb.db('myTestDatabase'),
          coll;
      
      // Create our new collection (like a MySQL table) and change the default
      // primary key from "_id" to "id"
      coll = db.collection('myCollection', {primaryKey: 'id'});
      
      // Insert our records into the collection
      coll.insert([
          {"name":"my Name","id":12,"type":"car owner"},
          {"name":"my Name2","id":13,"type":"car owner2"},
          {"name":"my Name4","id":14,"type":"car owner3"},
          {"name":"my Name4","id":15,"type":"car owner5"}
      ]);
      
      // Search the collection for the string "my nam" as a case insensitive
      // regular expression - this search will match all records because every
      // name field has the text "my Nam" in it
      var searchResultArray = coll.find({
          name: /my nam/i
      });
      
      console.log(searchResultArray);
      
      /* Outputs
      [
          {"name":"my Name","id":12,"type":"car owner"},
          {"name":"my Name2","id":13,"type":"car owner2"},
          {"name":"my Name4","id":14,"type":"car owner3"},
          {"name":"my Name4","id":15,"type":"car owner5"}
      ]
      */
      

      免责声明:我是 ForerunnerDB 的开发者。

      【讨论】:

        【解决方案4】:

        Zapping - 你可以使用这个 javascript 库;反抗JS。无需将 JSON 数据重组为对象以简化搜索。相反,您可以使用这样的 XPath 表达式搜索 JSON 结构:

            var data = [
           {
              "id": "one",
              "pId": "foo1",
              "cId": "bar1"
           },
           {
              "id": "two",
              "pId": "foo2",
              "cId": "bar2"
           },
           {
              "id": "three",
              "pId": "foo3",
              "cId": "bar3"
           }
        ],
        res = JSON.search( data, '//*[id="one"]' );
        
        console.log( res[0].cId );
        // 'bar1'
        

        DefiantJS 用一个新方法扩展了全局对象 JSON; “搜索”返回匹配的数组(如果没有找到则为空数组)。您可以通过在此处粘贴 JSON 数据并测试不同的 XPath 查询来亲自尝试:

        http://www.defiantjs.com/#xpath_evaluator

        如您所知,XPath 是一种标准化的查询语言。

        【讨论】:

        • 谢谢,下次记得试试这个。
        【解决方案5】:

        我遇到了一个包含多个嵌套对象的复杂模型的问题。我正在研究的一个很好的例子是:假设你有一张自己的宝丽来。然后将那张照片放入汽车的后备箱中。汽车在一个大箱子里。板条箱与许多其他板条箱一起放在一艘大船上。我不得不搜查货舱,查看板条箱,检查行李箱,然后寻找一张现有的我的照片。

        我在网上找不到任何好的解决方案可以使用,并且使用.filter() 仅适用于数组。大多数解决方案建议只检查model["yourpicture"] 是否存在。这是非常不可取的,因为从示例中,这只会搜索船的货舱,我需要一种方法将它们从更远的兔子洞中取出。

        这是我提出的递归解决方案。在 cmets,我从 T.J. 确认。 Crowder 认为需要递归版本。我想我会分享它以防万一有人遇到类似的复杂情况。

        function ContainsKeyValue( obj, key, value ){
            if( obj[key] === value ) return true;
            for( all in obj )
            {
                if( obj[all] != null && obj[all][key] === value ){
                    return true;
                }
                if( typeof obj[all] == "object" && obj[all]!= null ){
                    var found = ContainsKeyValue( obj[all], key, value );
                    if( found == true ) return true;
                }
            }
            return false;
        }
        

        这将从图中的给定对象开始,并向下递归找到的任何对象。我是这样使用的:

        var liveData = [];
        for( var items in viewmodel.Crates )
        {
            if( ContainsKeyValue( viewmodel.Crates[items], "PictureId", 6 ) === true )
            {
                liveData.push( viewmodel.Crates[items] );
            }
        }
        

        这将产生一个包含我的图片的板条箱数组。

        【讨论】:

          【解决方案6】:

          (您不是在搜索“JSON”,而是在搜索一个数组——JSON 字符串已经被反序列化为一个对象图,在本例中是一个数组。)

          一些选项:

          使用对象而不是数组

          如果你可以控制这个东西的生成,它必须是一个数组吗?因为如果没有,还有更简单的方法。

          说这是你的原始数据:

          [
              {"id": "one",   "pId": "foo1", "cId": "bar1"},
              {"id": "two",   "pId": "foo2", "cId": "bar2"},
              {"id": "three", "pId": "foo3", "cId": "bar3"}
          ]
          

          您可以改为执行以下操作吗?

          {
              "one":   {"pId": "foo1", "cId": "bar1"},
              "two":   {"pId": "foo2", "cId": "bar2"},
              "three": {"pId": "foo3", "cId": "bar3"}
          }
          

          然后通过 ID 找到相关条目是微不足道的:

          id = "one"; // Or whatever
          var entry = objJsonResp[id];
          

          ...正在更新它:

          objJsonResp[id] = /* New value */;
          

          ...并删除它:

          delete objJsonResp[id];
          

          这利用了这样一个事实,即在 JavaScript 中,您可以使用属性名称作为字符串来索引对象——该字符串可以是文字,也可以来自变量,如上面的 id

          放入 ID-to-Index 映射

          (愚蠢的想法,早于上述内容。出于历史原因保留。)

          看起来您需要将其作为一个数组,在这种情况下,没有比搜索数组更好的方法,除非您想在其上放置地图,如果您可以控制对象的生成。例如,假设您最初有这个:

          [
              {"id": "one",   "pId": "foo1", "cId": "bar1"},
              {"id": "two",   "pId": "foo2", "cId": "bar2"},
              {"id": "three", "pId": "foo3", "cId": "bar3"}
          ]
          

          生成代码可以提供一个 id-to-index 映射:

          {
              "index": {
                  "one": 0, "two": 1, "three": 2
              },
              "data": [
                  {"id": "one",   "pId": "foo1", "cId": "bar1"},
                  {"id": "two",   "pId": "foo2", "cId": "bar2"},
                  {"id": "three", "pId": "foo3", "cId": "bar3"}
              ]
          }
          

          然后在变量id 中获取 id 的条目很简单:

          var index = objJsonResp.index[id];
          var obj = objJsonResp.data[index];
          

          这利用了您可以使用属性名称对对象进行索引这一事实。

          当然,如果你这样做,你必须在修改数组时更新地图,这可能会成为维护问题。

          但是,如果您无法控制对象的生成,或者更新 ids-to-indexes 的映射是太多代码和/或维护问题,那么您将不得不进行强力搜索。

          蛮力搜索(已更正)

          有点 OT(尽管您确实询问是否有更好的方法 :-)),但您用于循环数组的代码不正确。 Details here,但您不能使用 for..in 循环遍历数组索引(或者更确切地说,如果这样做,您必须特别费力地这样做); for..in 循环遍历对象的属性,而不是数组的索引。使用非稀疏数组(而您的数组是非稀疏数组)的最佳选择是标准的老式循环:

          var k;
          for (k = 0; k < someArray.length; ++k) { /* ... */ }
          

          var k;
          for (k = someArray.length - 1; k >= 0; --k) { /* ... */ }
          

          你喜欢哪个(后者在所有实现中并不总是更快,这对我来说是违反直觉的,但我们就是这样)。 (对于 sparse 数组,您可以使用 for..in,但要再次特别努力避免陷阱;更多内容请参阅上面链接的文章。)

          在数组上使用for..in似乎在简单的情况下工作,因为数组的每个索引都有属性,并且它们唯一的其他默认属性(length 及其方法)被标记为不可枚举。但是,一旦您在数组对象上设置(或框架设置)任何其他属性,它就会中断(这是完全有效的;数组只是对 length 属性进行了一些特殊处理的对象)。

          【讨论】:

          • 谢谢,我会采取第一种方式似乎更好。
          • 很棒的技巧:for(var i=someArray.length;0
          • @TJCrowder - 这不适用于真正的对象图。您只覆盖一层(顶部)。如果此模型更复杂,则不会找到嵌套值。
          • @TravisJ:非常正确。 OP 的数据没有嵌套。如果数据是嵌套的,则很容易制作上述的递归版本。 :-)
          • @T.J.Crowder - 我原以为需要一个递归版本并且刚刚制作了一个。我建议给定的解决方案只是顶层,看看你的想法是什么。如果其他人有兴趣使用我在下面发布的递归版本。
          【解决方案7】:

          如果您的数组中的 JSON 数据以某种方式排序,那么您可以实现多种搜索。但是,如果您不处理大量数据,那么您可能会在这里使用 O(n) 操作(正如您所拥有的那样)。其他任何事情都可能是矫枉过正。

          【讨论】:

            猜你喜欢
            • 2010-12-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-08-03
            • 2019-01-07
            • 1970-01-01
            相关资源
            最近更新 更多