当一个参数未定义时,如何跳过第一次迭代?

How to skip over the first iteration when one argument is undefined?

提问人:Niels Koop 提问时间:8/23/2023 更新时间:8/24/2023 访问量:51

问:

我正在做一个基于下划线.js中的reduce练习的练习。现在,我有一个有效的解决方案,但我相信它可以做得更好,因为我在我的弗兰肯斯坦解决方案中使用了大量的复制粘贴。

我找到的解决方案

这是我提出的解决方案:

_.reduce = function (collection, iteratee, accumulator, context) {
  if (Array.isArray(collection)) {
    var returnVal = accumulator !== undefined ? accumulator : collection[0];
    if (accumulator !== undefined) {
      for (var i = 0; i < collection.length; i++) {
        returnVal = iteratee.call(
          context,
          returnVal,
          collection[i],
          i,
          collection
        );
      }
    } else {
      for (var i = 1; i < collection.length; i++) {
        returnVal = iteratee.call(
          context,
          returnVal,
          collection[i],
          i,
          collection
        );
      }
    }
    console.log(returnVal);
    return returnVal;
  } else if (typeof collection === "object") {
    var keys = Object.keys(collection);
    var returnVal =
      accumulator !== undefined ? accumulator : collection[keys[0]];
    if (accumulator !== undefined) {
      for (var i = 0; i < keys.length; i++) {
        returnVal = iteratee.call(
          context,
          returnVal,
          collection[keys[i]],
          keys[i],
          collection
        );
      }
    } else {
      for (var i = 1; i < keys.length; i++) {
        returnVal = iteratee.call(
          context,
          returnVal,
          collection[keys[i]],
          keys[i],
          collection
        );
      }
    }
    console.log(returnVal);
    return returnVal;
  }
};

正如你所看到的,我区分了作为数组的集合和作为对象的集合。这是我的第一个条件。在这个条件中,我对数组和对象都有另一个条件。此条件检查是否未定义。如果 ,则运行从第一次迭代开始,如果 ,则运行从第二次迭代开始。我这样做是因为当累加器未定义时,它必须是集合中的第一个值,如果我从第一次迭代开始运行,则第一个值将显示为双倍。accumulatortruefor loopfalsefor loopfor loop

我尝试过什么

我尝试在里面添加一个条件来检查是否是,如果是我会。然而,这不起作用,因为蓄能器显然会留下来。for loopaccumulatorundefinedundefinedcontinueundefined

我还尝试在 和 中添加该条件,并从第二次迭代开始,条件将手动将集合的第一个值添加到 (if 是 )。这也不起作用,因为它搞砸了结果并以错误的顺序返回。for loopfor loopreturnValaccumulatorundefined

我期待什么

我不知道这是否可能,但我想缩短我的代码以使其更具可读性,并且总体上更好的代码。我认为可以缩短这段代码,我只是看不出在哪里以及如何缩短。如果有任何其他改进需要改进,我很乐意听到它们,因为它将帮助我成为一个更好的编码员!

提前致谢。

我不能使用内置函数,如 、 和 。forEach()map()reduce()filter()

JavaScript 条件语句 下划线 .js

评论

0赞 mplungjan 8/23/2023
请点击编辑,然后点击代码片段编辑器,创建一个最小的可重现示例。例如,我们缺少迭代。显示要减少的事物的一些 console.logs[<>]

答:

1赞 Samuel Nandi 8/23/2023 #1

这里有一个简化的代码。它通过根据是否是设置初始索引来使用单个循环。如果未定义,则从 index 开始,跳过第一次迭代。如果定义,它将从 index 开始,包括第一次迭代。而且最好使用而不是 .foriaccumulatorundefinedaccumulator1accumulator0letvar

_.reduce = function (collection, iteratee, accumulator, context) {
    let returnVal, i, keys;
    
    if (Array.isArray(collection)) {
      returnVal = accumulator !== undefined ? accumulator : collection[0];
      i = accumulator !== undefined ? 0 : 1;
      
      for (; i < collection.length; i++) {
        returnVal = iteratee.call(context, returnVal, collection[i], i, collection);
      }
      
      return returnVal;
    } 
    
    if (typeof collection === "object") {
      keys = Object.keys(collection);
      returnVal = accumulator !== undefined ? accumulator : collection[keys[0]];
      i = accumulator !== undefined ? 0 : 1;
      
      for (; i < keys.length; i++) {
        returnVal = iteratee.call(context, returnVal, collection[keys[i]], keys[i], collection);
      }
      
      return returnVal;
    }
  };

评论

0赞 Niels Koop 8/23/2023
谢谢!这奏效了。实际上,将 i 变量放在三元运算符中非常容易:-D
1赞 Julian 8/24/2023 #2

本着函数式编程的真正精神,你可以通过构建已经大致满足你想要的代码来大幅减少(没有双关语的意思)。你想要的是迭代一个对象或一个数组(或 Underscore 将来可能支持的任何其他类型的集合),并且已经这样做了。_.find_.each

至于专门处理第一次迭代,状态变量在这种情况下效果很好。这种方法可能如下所示:

function reduce(collection, iteratee, accumulator, context) {
    // The state variable, boolean
    var specialFirst = (accumulator == undefined);
    _.find(collection, function(value, key) {
        if (specialFirst) {
            accumulator = value;
            specialFirst = false;
        } else {
            accumulator = iteratee.call(context, accumulator, value, key, collection);
        }
    }, context);
    return accumulator;
}

function add(left, right) {
    return left + right;
}

console.log(reduce([1, 2, 3], add));
console.log(reduce([1, 2, 3], add, 2));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>

(我不知道为什么最后会出现脚本错误,但它可能特定于代码段运行器。

巧合的是,我曾在 Underscore 的一个分支工作过,在那里我做了一些类似的事情,但更复杂。迭代者会暂时更改标识,以实现相同的最终结果,因此可以说迭代者本身就是状态变量。你可以在这里阅读:https://github.com/jashkenas/underscore/blob/eaba5b58fa8fd788a5be1cf3b66e81f8293f70f9/modules/_createReduce.js

来自当前 Underscore 维护者的问候,祝您在发现 Underscore 和函数式编程时好运!