在 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的主要内容,如果未能解决你的问题,请参考以下文章