首页 > 解决方案 > 如何根据 prop 值搜索和获取节点?

问题描述

大家好,我想根据对象中的道具值提取一个节点,这就是我想要的

笔记:

  1. uniqueId ---> 是我想要的键。
  2. items ---> 是所有嵌套对象的固定键
  3. ['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. 案例 1:如果我想搜索['home']结果将是

const result = {
  home: {
    label: 'home',
    alt: 'home',
    uniqueId: 'home', 
  }
}

  1. 情况 2 如果要搜索嵌套对象['announcements'],则结果应为

const result = {
  cms: {
    label: 'cms',
    alt: 'cms',
    uniqueId: 'cms',
    items: {
      announcements: {
        label: 'cmsAnnouncementsLabel',
        alt: 'cms-announcements',
        uniqueId: 'announcements',
      },
    }
  },
}

请注意, 新闻被忽略,因为它不在搜索数组中

  1. 案例 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 在这里相同的问题,但他们忽略了父节点道具

我怎样才能做到这一点?

标签: javascriptlodash

解决方案


一种可能的实现

我敢说这个问题可能比看起来更复杂。我尝试使用 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对象的顺序:

  1. 查看 IIE(立即调用表达式)内部,我使用它来界定我的范围并避免全局范围污染。
  2. 您应该首先查看的是_findDeep函数。算法的第一部分在那里处理。
  3. 它调用_findDeepHelper,它执行以下操作:
    1. 搜索关键字位于使用递归算法的对象中
    2. 找到密钥后,它会遍历树。
    3. 上行时返回当前节点的键名
    4. 结果,我们取回了包含我们正在寻找的键的对象根中最上层节点的键
  4. 然后,使用 Lodash_.pick创建一个新对象,仅选择包含我们要查找的键的根键
  5. 最后,使用_deleteSibilings它删除我们正在寻找的键的所有兄弟
  6. 我们现在有了结果对象!

搜索多个键

那么,如何搜索多个键呢?

  1. 为了简化,我mergeDeepLeft从 Rambda 库中分叉并更改了一些函数,以便它依赖于 Lodash
  2. 我分别进行每个搜索,然后使用mergeDeepLeft将查询合并在一起
  3. 如果存在冲突的键,则搜索键数组左侧的键所产生的数据将丢失,仅保留右侧的键所产生的键

为什么还有我没有要求的功能?

因为我在做这件事,所以我只花了一点额外的时间,并制作了我称之为 findDeep 的结果算法,在不同的用例中使用起来更加灵活和方便。这只生成了一点额外的代码,但我希望它可以为您提供更好的服务。

感谢您的阅读!

我很乐意提供任何进一步的解释,你可能需要它。作为奖励,我要补充一点,可以编写一些巧妙的实用函数作为 lowdb/lodash mixin,以使我们能够使用更短的语法并仍然正确链接。但是,这可能比您要寻找的要多。


推荐阅读