javascript - 如何根据 prop 值搜索和获取节点?
问题描述
大家好,我想根据对象中的道具值提取一个节点,这就是我想要的
笔记:
- uniqueId ---> 是我想要的键。
- items ---> 是所有嵌套对象的固定键
- ['item1', ...etc] ----> 要在对象内部搜索的道具数组,如下所述
我的对象是:
const obj = {
home: {
label: 'home',
alt: 'home',
uniqueId: 'home',
},
tasks: {
label: 'tasks',
alt: 'tasks',
uniqueId: 'tasks',
},
cms: {
label: 'cms',
alt: 'cms',
uniqueId: 'cms',
items: {
news: {
label:'news',
alt: 'cms-news',
uniqueId: 'news',
},
announcements: {
label: 'cmsAnnouncementsLabel',
alt: 'cms-announcements',
uniqueId: 'announcements',
},
}
},
supervision: {
label: 'supervision',
alt: 'supervision',
uniqueId: 'supervision',
items: {
violations: {
label: 'violationsManegment',
alt: 'supervisors',
uniqueId: 'violations',
items: {
violationTemplates: {
label: 'violationTemplates',
alt: 'violations',
uniqueId: 'violationTemplates',
},
paradigms: {
label: 'paradimsManegment',
alt: 'paradims',
uniqueId: 'paradigms',
},
}
}
}
}
}
预期成绩:
- 案例 1:如果我想搜索['home']结果将是
const result = {
home: {
label: 'home',
alt: 'home',
uniqueId: 'home',
}
}
- 情况 2 如果要搜索嵌套对象['announcements'],则结果应为
const result = {
cms: {
label: 'cms',
alt: 'cms',
uniqueId: 'cms',
items: {
announcements: {
label: 'cmsAnnouncementsLabel',
alt: 'cms-announcements',
uniqueId: 'announcements',
},
}
},
}
请注意, 新闻被忽略,因为它不在搜索数组中
- 案例 3 如果我想搜索['paradigms']结果将是
const result = {
supervision: {
label: 'supervision',
alt: 'supervision',
uniqueId: 'supervision',
items: {
violations: {
label: 'violationsManegment',
alt: 'supervisors',
uniqueId: 'violations',
items: {
paradigms: {
label: 'paradimsManegment',
alt: 'paradims',
uniqueId: 'paradigms',
},
}
}
}
}
}
违规模板被忽略
4.最后一种情况,如果我想搜索['violationTemplates', 'home', 'paradigms']
const result = {
home: {
label: 'home',
alt: 'home',
uniqueId: 'home',
},
tasks: {
label: 'tasks',
alt: 'tasks',
uniqueId: 'tasks',
},
supervision: {
label: 'supervision',
alt: 'supervision',
uniqueId: 'supervision',
items: {
violations: {
label: 'violationsManegment',
alt: 'supervisors',
uniqueId: 'violations',
items: {
violationTemplates: {
label: 'violationTemplates',
alt: 'violations',
uniqueId: 'violationTemplates',
},
paradigms: {
label: 'paradimsManegment',
alt: 'paradims',
uniqueId: 'paradigms',
},
}
}
}
}
}
**我使用 deepDash ** ----> findDeep 和 filterDeep 在这里相同的问题,但他们忽略了父节点道具
我怎样才能做到这一点?
解决方案
一种可能的实现
我敢说这个问题可能比看起来更复杂。我尝试使用 Lodash 创建您给我们的规范的实现,并分叉Rambda mergeDeepLeft 将其移植到Lodash _.has
和_.isObject
函数。生成的代码没有经过优化,而且很长,但我希望它可以为您提供一个起点,如果您需要更高性能的算法,您可以在该起点上进行优化。
我可以快速尝试浏览器中的功能吗?
当然!我实际上做了一个 Codepen,你可以在其中尝试算法,只要记得打开浏览器控制台查看测试结果即可!链接在这里:
https://codepen.io/jhack_jos/pen/ZEBYPbR
如何使用它
//the function signature
result = findDeep(object, query, options);
在哪里:
object是要查看的对象
查询可以是:
- 一个字符串,在这种情况下将被视为搜索的关键
- 一个数组,在这种情况下,每个元素都将被视为搜索的键
options是一个可选对象,可能包含以下一个或两个键:
{merge: "yes", keeptree: "yes"}
merge: "yes"
告诉 findDeep 对结果数组执行 mergeDeepLeft,这样:a) 没有它,您将返回一个数组,其中包含您搜索的每个键的搜索结果 b) 您将获得一个合并对象。keeptree: "yes"
告诉 findDeep 返回整个树,而不仅仅是附加到我们正在寻找的一个或多个键上的对象。
对于您的用例,您应该始终使用这两个选项调用 findDeep。比如像这样:
result = findDeep(obj, ["key1", "key2"], {merge: "yes", keeptree: "yes"}
结果可以是以下之一:
- 搜索到的键指向的对象(一个键,没有选项)
- 包含搜索键指向的对象列表的数组(多个键,无选项)
- 一个对象,包含所有键的所有合并结果(多个键,
options = {merge: yes"}
- 通过指定
options = {keeptree: "yes"}
,您还将获得搜索键层次结构中的键。
使用实例
我可以看到我的解释可能很做作,这就是为什么我准备了一系列示例,希望能阐明该函数的使用方式。首先,我向您介绍测试数据集,这是您在问题中提供给我们的数据集:
测试数据集
/* Test dataset */
const dataset = {
home: {
label: 'home',
alt: 'home',
uniqueId: 'home',
},
tasks: {
label: 'tasks',
alt: 'tasks',
uniqueId: 'tasks',
},
cms: {
label: 'cms',
alt: 'cms',
uniqueId: 'cms',
items: {
news: {
label:'news',
alt: 'cms-news',
uniqueId: 'news',
},
announcements: {
label: 'cmsAnnouncementsLabel',
alt: 'cms-announcements',
uniqueId: 'announcements',
},
}
},
supervision: {
label: 'supervision',
alt: 'supervision',
uniqueId: 'supervision',
items: {
violations: {
label: 'violationsManegment',
alt: 'supervisors',
uniqueId: 'violations',
items: {
violationTemplates: {
label: 'violationTemplates',
alt: 'violations',
uniqueId: 'violationTemplates',
},
paradigms: {
label: 'paradimsManegment',
alt: 'paradims',
uniqueId: 'paradigms',
},
}
}
}
}
};
例子
情况1
//CASE 1
result = findDeep(dataset, "home", {keeptree: "yes"});
result = {
"label": "tasks",
"alt": "tasks",
"uniqueId": "tasks",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
},
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
}
}
},
"announcements": {
"label": "cmsAnnouncementsLabel",
"alt": "cms-announcements",
"uniqueId": "announcements"
}
}
}
案例二
//CASE 2
result = findDeep(dataset, "announcements", {keeptree: "yes"});
result = {
"cms": {
"label": "cms",
"alt": "cms",
"uniqueId": "cms",
"items": {
"announcements": {
"label": "cmsAnnouncementsLabel",
"alt": "cms-announcements",
"uniqueId": "announcements"
}
}
}
}
案例 3
//CASE 3
result = findDeep(dataset, "paradigms", {keeptree: "yes"});
result = {
"supervision": {
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
}
}
}
}
}
}
案例 4
//CASE 4
result = findDeep(dataset, ['violationTemplates', 'home', 'paradigms'], {merge: "yes", keeptree: "yes"});
result = {
"supervision": {
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
},
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
}
}
}
}
},
"home": {
"label": "home",
"alt": "home",
"uniqueId": "home"
}
}
案例 4b
//CASE 4b
result = findDeep(dataset, ['home', 'paradigms'], {merge: "yes", keeptree: "yes"});
result = {
"supervision": {
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
}
}
}
}
},
"home": {
"label": "home",
"alt": "home",
"uniqueId": "home"
}
}
额外内容之下:该功能不需要做的事情。
额外 1
//EXTRA 1
result = findDeep(dataset, "violationTemplates");
result = {
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
}
}
}
}
}
额外 2
//EXTRA 2
result = findDeep(dataset, ["supervision", "violationTemplates", "announcements", "tasks"]);
result = [
{
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
},
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
}
}
}
}
},
{
"label": "supervision",
"alt": "supervision",
"uniqueId": "supervision",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
}
}
}
}
},
{
"label": "cms",
"alt": "cms",
"uniqueId": "cms",
"items": {
"announcements": {
"label": "cmsAnnouncementsLabel",
"alt": "cms-announcements",
"uniqueId": "announcements"
}
}
},
{
"label": "tasks",
"alt": "tasks",
"uniqueId": "tasks"
}
]
额外 3
//EXTRA 3
result = findDeep(dataset, ["supervision", "violationTemplates", "announcements", "tasks"], {merge: "yes"});
result = {
"label": "tasks",
"alt": "tasks",
"uniqueId": "tasks",
"items": {
"violations": {
"label": "violationsManegment",
"alt": "supervisors",
"uniqueId": "violations",
"items": {
"violationTemplates": {
"label": "violationTemplates",
"alt": "violations",
"uniqueId": "violationTemplates"
},
"paradigms": {
"label": "paradimsManegment",
"alt": "paradims",
"uniqueId": "paradigms"
}
}
},
"announcements": {
"label": "cmsAnnouncementsLabel",
"alt": "cms-announcements",
"uniqueId": "announcements"
}
}
}
源代码
const findDeep = ( () => {
/* Libraries */
/* This code was forked from Rambda by Jacopo Tedeschi. Dependencies were adjusted so that it depends on Lodash instead.
It wouldn't be hard to even make it completely library independent */
const mergeWithKey = function mergeWithKey(fn, l, r) {
var result = {};
var k;
for (k in l) {
if (_.has(l, k)) {
if(_.has(r, k))
{
result[k] = fn(k, l[k], r[k]);
} else {
result[k] = l[k];
}
}
}
for (k in r) {
if (_.has(r, k) && !(_.has(result, k))) {
result[k] = r[k];
}
}
return result;
};
const mergeDeepWithKey = function mergeDeepWithKey(fn, lObj, rObj) {
return mergeWithKey(function(k, lVal, rVal) {
if (_.isObject(lVal) && _.isObject(rVal)) {
return mergeDeepWithKey(fn, lVal, rVal);
} else {
return fn(k, lVal, rVal);
}
}, lObj, rObj);
};
const mergeDeepLeft = function mergeDeepLeft(lObj, rObj) {
return mergeDeepWithKey(function(k, lVal, rVal) {
return lVal;
}, lObj, rObj);
};
/* Code below here was written by Jacopo Tedeschi */
function _deleteSibilings(object, searchKey) {
return _.has(object, searchKey)
? {[searchKey] : object[searchKey]}
: _.reduce(object, (acc, value, key) => {
acc[key] = _.isObject(value)
? _deleteSibilings(value, searchKey)
: value;
return acc;
}, {});
};
function _findDeepHelper(collection, key, options)
{
if(_.has(collection, key))
{
//here we make sure we also return the key
return _.get(options, "keeptree") == "yes"
? key
: collection[key];
} else
{
const resultKey = _.findKey(collection,
value => _.isObjectLike(value)
&& _findDeepHelper(value, key, options));
return _.get(options, "keeptree") == "yes"
? resultKey
: _.get(collection, resultKey);
}
}
function _findDeep(collection, key, options)
{
let result = _findDeepHelper(collection, key, options);
if(_.get(options, "keeptree") == "yes")
{
result = _.pick(collection, result);
}
return _deleteSibilings(result, key);
}
function _findMultipleDeep(collection, keys, options)
{
return _.isArray(keys)
? _.map(keys, key => _findDeep(collection, key, options))
: _findDeep(collection, keys, options);
}
function _mergeQueries(queries)
{
if(_.isArray(queries))
{
return queries.length > 1
? _.reduce(queries, mergeDeepLeft)
: _.get(queries, 0);
} else
{
return queries;
}
}
return (collection, keys, options) =>
{
const result = _findMultipleDeep(collection, keys, options);
return _.get(options, "merge") == "yes"
? _mergeQueries(result)
: result;
};
})();
算法解释
仅搜索 1 个键
这就是我在算法中创建所需result
对象的顺序:
- 查看 IIE(立即调用表达式)内部,我使用它来界定我的范围并避免全局范围污染。
- 您应该首先查看的是
_findDeep
函数。算法的第一部分在那里处理。 - 它调用
_findDeepHelper
,它执行以下操作:- 搜索关键字位于使用递归算法的对象中
- 找到密钥后,它会遍历树。
- 上行时返回当前节点的键名
- 结果,我们取回了包含我们正在寻找的键的对象根中最上层节点的键
- 然后,使用 Lodash
_.pick
创建一个新对象,仅选择包含我们要查找的键的根键 - 最后,使用
_deleteSibilings
它删除我们正在寻找的键的所有兄弟 - 我们现在有了结果对象!
搜索多个键
那么,如何搜索多个键呢?
- 为了简化,我
mergeDeepLeft
从 Rambda 库中分叉并更改了一些函数,以便它依赖于 Lodash - 我分别进行每个搜索,然后使用
mergeDeepLeft
将查询合并在一起 - 如果存在冲突的键,则搜索键数组左侧的键所产生的数据将丢失,仅保留右侧的键所产生的键
为什么还有我没有要求的功能?
因为我在做这件事,所以我只花了一点额外的时间,并制作了我称之为 findDeep 的结果算法,在不同的用例中使用起来更加灵活和方便。这只生成了一点额外的代码,但我希望它可以为您提供更好的服务。
感谢您的阅读!
我很乐意提供任何进一步的解释,你可能需要它。作为奖励,我要补充一点,可以编写一些巧妙的实用函数作为 lowdb/lodash mixin,以使我们能够使用更短的语法并仍然正确链接。但是,这可能比您要寻找的要多。
推荐阅读
- c++ - spdlog 错误:“不知道如何格式化类型,包括 fmt/ostream.h 如果它提供了应该使用的 operator<<”
- php - 用户登录无效
- git - 在我的本地存储库中的特定文件上设置行尾
- ios - 使用 firebase 搜索用户订单并按日期排序(最新的优先)
- angular - 已经具有 !important 的覆盖样式
- kubernetes - 在 Kubernetes 中一次性或定期运行容器
- unix - 如何将证书 CN 名称定义为变量
- javascript - 同步获取已解决 Promise 的值
- regex - 删除指定字符之间的所有内容,包括多行
- c# - 缺少列的 Entity Framework Core 默认值