提问人:Niels Koop 提问时间:8/23/2023 更新时间:8/24/2023 访问量:51
当一个参数未定义时,如何跳过第一次迭代?
How to skip over the first iteration when one argument is undefined?
问:
我正在做一个基于下划线.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;
}
};
正如你所看到的,我区分了作为数组的集合和作为对象的集合。这是我的第一个条件。在这个条件中,我对数组和对象都有另一个条件。此条件检查是否未定义。如果 ,则运行从第一次迭代开始,如果 ,则运行从第二次迭代开始。我这样做是因为当累加器未定义时,它必须是集合中的第一个值,如果我从第一次迭代开始运行,则第一个值将显示为双倍。accumulator
true
for loop
false
for loop
for loop
我尝试过什么
我尝试在里面添加一个条件来检查是否是,如果是我会。然而,这不起作用,因为蓄能器显然会留下来。for loop
accumulator
undefined
undefined
continue
undefined
我还尝试在 和 中添加该条件,并从第二次迭代开始,条件将手动将集合的第一个值添加到 (if 是 )。这也不起作用,因为它搞砸了结果并以错误的顺序返回。for loop
for loop
returnVal
accumulator
undefined
我期待什么
我不知道这是否可能,但我想缩短我的代码以使其更具可读性,并且总体上更好的代码。我认为可以缩短这段代码,我只是看不出在哪里以及如何缩短。如果有任何其他改进需要改进,我很乐意听到它们,因为它将帮助我成为一个更好的编码员!
提前致谢。
我不能使用内置函数,如 、 和 。forEach()
map()
reduce()
filter()
答:
这里有一个简化的代码。它通过根据是否是设置初始索引来使用单个循环。如果未定义,则从 index 开始,跳过第一次迭代。如果定义,它将从 index 开始,包括第一次迭代。而且最好使用而不是 .for
i
accumulator
undefined
accumulator
1
accumulator
0
let
var
_.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;
}
};
评论
本着函数式编程的真正精神,你可以通过构建已经大致满足你想要的代码来大幅减少(没有双关语的意思)。你想要的是迭代一个对象或一个数组(或 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 和函数式编程时好运!
评论
[<>]