在 JavaScript 中查找 JSON

Posted

技术标签:

【中文标题】在 JavaScript 中查找 JSON【英文标题】:JSON find in JavaScript 【发布时间】: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 似乎更新了它们。 【参考方案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) 操作(正如您所拥有的那样)。其他任何事情都可能是矫枉过正。

【讨论】:

以上是关于在 JavaScript 中查找 JSON的主要内容,如果未能解决你的问题,请参考以下文章

如何在javascript中使用表达式查找'/'字符

在 Array、Javascript 中查找重复项

在 JavaScript 中查找 JSON

在javascript中查找和区分单词[关闭]

javascript 如何在javascript数组中按键和值查找对象的索引

如何在 JavaScript 中查找星期几