【问题标题】:Trying to get exact keys based on the given value in the nested object - Javascript (using recursion)尝试根据嵌套对象中的给定值获取确切的键 - Javascript(使用递归)
【发布时间】:2021-04-28 07:23:24
【问题描述】:

我有一个带有嵌套对象的对象,例如:

let menu = {
    vegetarian: {
        vegStarter: {
            plainPizza: 100,
            cheesePizza: 200,
            onionPizza: 200
        },
    },
    nonVegetarian: {
        nonVegStarter: {
            meatLoversPizza: 160,
            chickenPizza: 200,
            chilliMeatPizza: 200
        },
        nonVegMainCourse: {
            seafoodPizza: 300,
            spinachEggPizza: 200,
            eggPizza: 250
        }
    }

};

我需要收集所有具有相同值的键,并且必须使用递归。我的值是 200。我用 find()filter() 方法尝试了代码,但没有得到想要的结果。下面是带有输出的代码:

function searchUsingPriceFromMainMenu(mainObject) {
    let arrOfKeys = [];
    Object.values(mainObject).forEach(val => {
        if (val === 200 && typeof val !== "object") {
            arrOfKeys = Object.keys(mainObject).filter(key => mainObject[key] === val);
            document.write(arrOfKeys + "<br>")
        } else {
            if (typeof val === "object") {
                searchUsingPriceFromMainMenu(val, arrOfKeys)
            }
        }
    });
}
searchUsingPriceFromMainMenu(menu);

使用 filter() 方法:

cheesePizza,onionPizza
cheesePizza,onionPizza
chickenPizza,chilliMeatPizza
chickenPizza,chilliMeatPizza
spinachEggPizza

使用 find() 方法:只是用 find 代替了过滤器。代码同上。

cheesePizza
cheesePizza
chickenPizza
chickenPizza
spinachEggPizza

但我想要类似的结果:

cheesePizza
onionPizza
chickenPizza
chilliMeatPizza
spinachEggPizza

那么,是我遗漏了什么还是有其他方法可以得到相同的东西?请帮忙。

【问题讨论】:

    标签: javascript arrays recursion javascript-objects nested-object


    【解决方案1】:

    您可以获取对象的条目,并为每个条目检查它是否是对象。如果是,您可以调用递归函数来处理子对象。您可以将此结果连接到结果数组res,该数组在函数末尾返回。否则,如果它不是一个对象,那么您可以检查披萨值是否等于200(即:val),如果是,则将其推送到结果中。

    const menu = {vegetarian: { vegStarter: { plainPizza: 100, cheesePizza: 200, onionPizza: 200 }, }, nonVegetarian: { nonVegStarter: { meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200 }, nonVegMainCourse: { seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250 }}};
    
    const getPizzasByValue = (menu, val) => {
      let res = [];
      Object.entries(menu).forEach(([pizza, pizzaVal]) => {
        if(Object(pizzaVal) === pizzaVal) { // check if value if object
          res = res.concat(getPizzasByValue(pizzaVal, val));
        } else if(pizzaVal === val) { // if value is not object, check if it matches the value
          res.push(pizza);
        }
      }); 
      return res;
    }
    
    console.log(getPizzasByValue(menu, 200));

    【讨论】:

    • 谢谢@NickParsons。对于像我这样的初学者来说有点复杂,但在第四次尝试中就理解了。再次感谢。
    【解决方案2】:

    希望对您有所帮助 =)

    const menu = {
      vegetarian: {
        vegStarter: {
          plainPizza: 100,
          cheesePizza: 200,
          onionPizza: 200
        },
      },
      nonVegetarian: {
        nonVegStarter: {
          meatLoversPizza: 160,
          chickenPizza: 200,
          chilliMeatPizza: 200
        },
        nonVegMainCourse: {
          seafoodPizza: 300,
          spinachEggPizza: 200,
          eggPizza: 250
        }
      }
    };
    
    const search = (obj, checkedValue) => {
      const result = Object.entries(obj).flatMap(([key, value]) => {
        if (typeof value === 'object') {
          return search(value, checkedValue);
        }
        if (value === checkedValue) {
          return key;
        }
        return [];
      });
      return result;
    };
    
    const searched = search(menu, 200);
    console.log(searched);

    【讨论】:

    • 很好,如果您删除 return null 并将其更改为 return [];,您就可以删除额外的过滤器
    • @NickParsons,当然,谢谢 =) 我也喜欢你的答案,只是为了删除一个幻数。
    【解决方案3】:

    使用递归。

    const menu = {vegetarian: { vegStarter: { plainPizza: 100, cheesePizza: 200, onionPizza: 200 }, }, nonVegetarian: { nonVegStarter: { meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200 }, nonVegMainCourse: { seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250 }}};
    function recurse(menuObj, val, arr) {
      for (let key in menuObj) {
        
        if (typeof menuObj[key] === "object") {
           recurse(menuObj[key], val, arr);
        } else if (menuObj[key] === val){
           arr.push(key)
        }
      }
      
      return arr;
    }
    
    console.log(recurse(menu, 200, []))

    【讨论】:

      【解决方案4】:

      我们可以编写一个通用函数来根据谓词过滤输入对象的嵌套条目,然后在此基础上编写我们的菜单函数。这是一个实现:

      const filterEntries = (pred) => (obj) =>
        Object .entries (obj) .flatMap ((entry) => [
          ... (pred (entry) ? [entry] : []),
          ... (typeof entry [1] == 'object' ? filterEntries (pred) (entry [1]) : [])
        ])
      
      
      const matchingPrice = (price, menu) =>
        filterEntries (([k, v]) => v === price) (menu) 
          .map (([k, v]) => k)
      
      const menu = {vegetarian: {vegStarter: {plainPizza: 100, cheesePizza: 200, onionPizza: 200}}, nonVegetarian: {nonVegStarter: {meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200}, nonVegMainCourse: {seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250}}}
      
      console .log (matchingPrice (200, menu))

      关键是JS对象可以被认为是键值对的集合。所以这个对象:

      {
        plainPizza: 100,
        cheesePizza: 200,
        onionPizza: 200
      }
      

      可以认为包含这些键值对:

      [
        ['plainPizza', 100],
        ['cheesePizza', 200],
        ['onionPizza', 200]
      }
      

      还有这个:

      {
        nonVegStarter: {
          meatLoversPizza: 160,
          chickenPizza: 200,
          chilliMeatPizza: 200
        },
        nonVegMainCourse: {
          seafoodPizza: 300,
          spinachEggPizza: 200,
          eggPizza: 250
        }
      }
      

      由这两个键值对组成:

      [
        ['nonVegStarter', {meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200}],
        ['nonVegMainCourse', {seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250}]
      ]
      

      使用递归添加对象的所有嵌套子级,我们可以在对象中找到所有此类键值对的列表。它可能看起来像这样:

      [
        ["vegetarian", {vegStarter: {cheesePizza: 200, onionPizza: 200, plainPizza: 100}}],
        ["vegStarter", {cheesePizza: 200, onionPizza: 200, plainPizza: 100}],
        ["plainPizza", 100],
        ["cheesePizza", 200],
        ["onionPizza", 200],
        ["nonVegetarian", {nonVegStarter: {meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200}, nonVegMainCourse: {seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250}}],
        ["nonVegStarter", {meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200}],
        ["meatLoversPizza", 160],
        ["chickenPizza", 200],
        ["chilliMeatPizza", 200],
        ["nonVegMainCourse", {seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250}],
        ["seafoodPizza", 300],
        ["spinachEggPizza", 200],
        ["eggPizza", 250]
      ]
      

      我们的filterEntries 函数执行此操作并应用谓词函数(返回truefalse 的每个值)来查找对象中匹配某个条件的所有条目。它获取我们对象的每个条目并创建一个新的条目数组。如果谓词与给定条目匹配,则包含该条目,然后,如果条目处的值是一个对象,我们将使用相同的谓词和该值递归调用filterEntries,包括数组中的所有匹配结果。然后,它使用flatMap 将每个条目的数组组合成一个数组。

      然后我们在matchingPrice 中使用它,创建一个函数来测试一个条目以查看值是否与我们给定的价格匹配,并将它与我们的菜单对象一起传递给filterEntries。这将创建一个条目数组,如下所示:

      [
        ["cheesePizza", 200],
        ["onionPizza", 200],
        ["chickenPizza", 200],
        ["chilliMeatPizza", 200],
        ["spinachEggPizza", 200]
      ]
      

      最后的map 调用将这些转换为

      [
        "cheesePizza",
        "onionPizza",
        "chickenPizza",
        "chilliMeatPizza",
        "spinachEggPizza"
      ]
      

      filterEntries 略有不同的写法是一个很好的论据,将过滤分层放在一个单独的实用函数之上,该实用函数将所有条目生成为一个数组。这不会有太大的不同:

      const allEntries = (obj) =>
        Object .entries (obj) .flatMap ((entry) => [
          entry,
          ... (typeof entry [1] == 'object' ? allEntries (entry [1]) : [])
        ])
      
      const filterEntries = (pred) => (obj) =>
        allEntries (obj) .filter (pred)
      

      这样做的好处是可以创建两个有用的实用函数,而不仅仅是一个。

      【讨论】:

        【解决方案5】:

        这是我对使用 JavaScript 生成器的问题的看法。首先,我们从一个生成树的所有叶子的函数开始,t,并且只生成与price 输入匹配的叶子

        function* matchingPrice (t, price)
        { for (const [k,v] of leafs(t))
            if (v == price)
              yield k
        }
        

        现在我们编写一个 leafs 函数,它对输入 t 进行简单的类型分析,在 Objects 上重复出现,否则将输入 keyt 作为一对 -

        function* leafs (t, key)
        { switch (t?.constructor)
          { case Object:
              for (const [k,v] of Object.entries(t))
                yield* leafs(v, k)
              break
            default:
              yield [key ?? null, t]
          }
        }
        

        把它们放在一个可执行的sn-p中-

        function* matchingPrice (menu, price)
        { for (const [k,v] of leafs(menu))
            if (v == price)
              yield k
        }
        
        function* leafs (t, key)
        { switch (t?.constructor)
          { case Object:
              for (const [k,v] of Object.entries(t))
                yield* leafs(v, k)
              break
            default:
              yield [key ?? null, t]
          }
        }
        
        const menu =
          {vegetarian: {vegStarter: {plainPizza: 100, cheesePizza: 200, onionPizza: 200}}, nonVegetarian: {nonVegStarter: {meatLoversPizza: 160, chickenPizza: 200, chilliMeatPizza: 200}, nonVegMainCourse: {seafoodPizza: 300, spinachEggPizza: 200, eggPizza: 250}}}
        
        console.log(Array.from(matchingPrice(menu, 200)))

        输出 -

        [
          "cheesePizza",
          "onionPizza",
          "chickenPizza",
          "chilliMeatPizza",
          "spinachEggPizza"
        ]
        

        【讨论】:

          猜你喜欢
          • 2022-11-24
          • 1970-01-01
          • 2021-11-27
          • 2021-12-05
          • 1970-01-01
          • 2013-12-31
          • 1970-01-01
          • 1970-01-01
          • 2020-01-26
          相关资源
          最近更新 更多