【问题标题】:How to write a cheapestStoreForRecipe function in the most efficient way?如何以最有效的方式编写一个最便宜的StoreForRecipe 函数?
【发布时间】:2026-01-27 19:20:03
【问题描述】:

我如何以最有效的方式解决这个问题?我们应该使用 .reduce() 和这些方法,还是应该使用经典的 for in 循环来遍历 allStore 中的键并使用配方进行计算?

var soup = { //recipe
    potato: 3,
    onion: 1,
    corn: 5
};

var edoka = {
    cheese: 8,
    corn: 3,
    meat: 6,
    onion: 4,
    pea: 1,
    oregano: 7,
    potato: 5,
    tomato: 6
};

var were = {
    cheese: 6,
    corn: 2,
    meat: 9,
    onion: 5,
    pea: 2,
    oregano: 6,
    potato: 3,
    tomato: 3
};

var brutto = {
    cheese: 6,
    corn: 2,
    meat: 9,
    onion: 5,
    pea: 2,
    oregano: 8,
    potato: 3,
    tomato: 4
};

var allStores = { // this is an example of a "storeCollection"
    Brutto: brutto,
    Edoka: edoka,
    Were: were,
};

function cheapestStoreForRecipe(recipe, storeCollection){
    // make it return the key for the store in storeCollection
    // that has the cheapest total cost for recipe. Feel free
    // to use costOfRecipe inside this function!
}

【问题讨论】:

  • 我认为这是你的功课,你刚刚给了我们属于它的代码。您自己的代码在哪里以及您尝试解决的问题?

标签: javascript arrays function object


【解决方案1】:

这是解决问题的最有效方法:

function cheapestStoreForRecipe(recipe, storeCollection) {
    let cheapestStore;
    let cheapestPrice = Number.MAX_SAFE_INTEGER;

    Object.keys(storeCollection).forEach(store => {
        let costOfRecipe = 0;

        Object.keys(recipe).forEach(ingredient => {
            costOfRecipe += recipe[ingredient] || 0;
        });

        if (costOfRecipe < cheapestPrice) {
            cheapestPrice = costOfRecipe;
            cheapestStore = store;
        }
    });

    return cheapestStore;
}

有些人认为forforEach 更有效。其他人则说,在现代环境中,这不再是真的。为了可读性,我更喜欢 forEach 实现。

【讨论】:

    【解决方案2】:

    这里有一个解决方案,可以帮助您探索如何使用Array.reduce。它可能不是最有效的,但您可以从这里设计自己的解决方案。为了提高效率,您应该继续使用该概念以获得处理时间或复杂性方面所需的结果。

    Repl Example

    cheapestStoreForRecipe 接受两个参数:recipestoreCollection。该方法返回包含每个项目成本和总成本的配方发票的商店列表。

    首先,storeCollection 遍历集合中的每个商店。其次,对每个商店的配方项目进行迭代。当存在与商店库存项目匹配的配方项目时,计算项目的quantityunittotal 值;然后计算每个配方的总成本。

    function cheapestStoreForRecipe(recipe, storeCollection){
      return Object.entries(storeCollection)
        .reduce((_storeCollection, [storeName, storeInventory]) => {
          let storeInvoice = Object.entries(recipe)
            .reduce((_recipe, [itemName, itemQuantity]) => {
              let storeInventoryItem = storeInventory[itemName]
              if(storeInventoryItem) {
                _recipe.invoice[itemName] = {
                  quantity: itemQuantity,
                  unit: storeInventoryItem,
                  total: itemQuantity * storeInventoryItem,
                }
                _recipe.total += _recipe.invoice[itemName].total
              }
              return _recipe
            }, {
              invoice: {},
              total: 0,
            })
          _storeCollection[storeName] = storeInvoice
          return _storeCollection
        }, {
          Brutto: {},
          Edoka: {},
          Were: {},
        })
    }
    
    {
      "Brutto": {
        "invoice": {
          "potato": {
            "quantity": 3, 
            "unit": 3,
            "total": 9
          },
          "onion": {
            "quantity": 1,
            "unit": 5,
            "total": 5
          },
          "corn": {
            "quantity": 5,
            "unit": 2,
            "total": 10
          }
        },
        "total": 24
      },
      "Edoka": {
        "invoice": {
          "potato": {
            "quantity": 3,
            "unit": 5,
            "total": 15
          },
          "onion": {
            "quantity": 1,
            "unit": 4,
            "total": 4
          },
          "corn": {
            "quantity": 5,
            "unit": 3,
            "total": 15
          }
        },
        "total": 34
      },
      "Were": {
        "invoice": {
          "potato": {
            "quantity": 3,
            "unit": 3,
            "total": 9
          },
          "onion": {
            "quantity": 1,
            "unit": 5,
            "total": 5
          },
          "corn": {
            "quantity": 5,
            "unit": 2,
            "total": 10
          }
        },
        "total": 24
      }
    }
    

    【讨论】:

      【解决方案3】:

      我个人会这样做,它是最易读的 IMO(尽管这显然是主观的)。我希望性能与任何性能一样好,这是使用相当新的 Object.entries API 的好机会。

      function cheapestStoreForRecipe(recipe, storeCollection){
          let cheapest, cheapestStore;
          for (const [storeName, store] of Object.entries(allStores)) {
              let total = 0;
              for (const [ingredient, amnt] of Object.entries(recipe)) {
                  total += store[ingredient] * amnt;
              }
      
              if (!cheapest || total < cheapest) {
                  cheapest = total;
                  cheapestStore = storeName;
              }
          }
      
          return cheapestStore;
      }
      

      【讨论】:

        【解决方案4】:

        要计算配方的成本,您需要将由配方键的交集 和每个商店产品键组成的数组的值相加。但这是你的功课,你必须自己做。

        关于对数组元素求和的不同方法的效率,for 在长数组中是最有效的。

        我在下面做了一个小demo对比

        • 为..of
        • 为每个
        • 减少

        const add = (a, b) => a + b;
        
        const functionsObj = {
          usingFor: async(array) => {
        
            return new Promise(res => {
              let result = 0;
              for (let i = 0; i < array.length; i++) {
                result += array[i];
              }
              res(result);
            });
          },
          usingForeach: async(array) => {
            return new Promise(res => {
              let result = 0;
              array.forEach(number => {
                result += number;
              })
              res(result);
            });
          },
        
          usingReduce: async(array) => {
            return new Promise(res => {
              const result = array.reduce(add);
              res(result);
            });
          },
          usingForOf: async(array) => {
            return new Promise(res => {
              let result = 0;
              for (let i of array) {
                result += i;
              }
              res(result);
            });
          }
        };
        const Arr10M = [];
        for (let j = 0; j < 10000000; j++) {
          Arr10M.push(
            1 + parseInt(40 * Math.random(), 10)
          );
        }
        const Arr10K = Arr10M.slice(0, 10000);
        
        async function runTests(method, arr, attempts = 300) {
          let results = [];
          for (let attempt of arr.slice(0, attempts)) {
            performance.mark('start');
            await functionsObj[method](arr);
            performance.mark('end');
            results.push(performance.measure(method, 'start', 'end').duration);
            performance.clearMeasures();
            performance.clearMarks();
          }
          return new Promise(res => {
            let min = 1 * Number(Math.min(...results)).toFixed(6),
              max = 1 * Number(Math.max(...results)).toFixed(6);
        
            window.setTimeout(() => {
              res([min, max]);
            }, 1000 - 1 * max);
          });
        
        }
        (async() => {
        
        
          let results = {},
            methods = ['usingFor', 'usingForOf', 'usingReduce', 'usingForeach'];
          for (let method of methods) {
            let [
              min_10K_elements,
              max_10K_elements
            ] = await runTests(method, Arr10K), [
                min_10M_elements,
                max_10M_elements
              ] = await runTests(method, Arr10M, 3),
              result = {
                min_10K_elements,
                max_10K_elements,
                min_10M_elements,
                max_10M_elements
              };
        
            results[method] = result;
            console.log({
              method,
              ...result
            });
          }
          console.table(results);
        
        
          return;
        })();

        如果您在浏览器中打开开发工具,在表格中查看结果,您会注意到for 始终是最快的,而reduceforEach 是+- 相等的。在测试 10K 元素的数组时,差异可以忽略不计(并且可能会受到浏览器甚至您机器中的并发进程的影响)。

        测试一个包含 10M 元素的数组,与 reduceforEach 相比,for 的性能在 20 倍到 30 倍之间。

        干净的代码胜过早期的过度优化,但如果您的作业获得原始性能,那么您现在就有了真正的基准。

        【讨论】:

          最近更新 更多