JavaScript中Array.reduce()的相反操作是什么?

3

Array.reduce()接受一个数组,并将数组中的元素与累加器结合,直到所有元素都被使用。

是否有一种函数(在其他语言中通常称为“unfold”),它从一个值开始生成元素,直到生成完整的数组(累加器用尽)?

我正在尝试在转换任意进制时完成此操作。 我目前的代码如下,但我想消除原始循环。

var dstAlphabet = "0123456789ABCDEFGH";
var dstBase = dstAlphabet.length;
var wet = BigInteger(100308923948716816384684613592839);
var digits_reversed = [];
while (wet.isPositive())
{
  // var digitVal = wet % dstBase
  var divRem = wet.divRem(dstBase); // [result of division, remainder]
  wet = divRem[0];
  digits_reversed.push(dstAlphabet.charAt(divRem[1].toJSValue()));
}

return digits_reversed.reverse().join("");

2
“直到生成完整的数组” --- 你能提供一个正式的定义吗?什么是“完整的数组”?那个假设的函数如何知道数组是否已经完整? - zerkms
zerkms:也许你可以提供第二个函数来确定该值是否已被耗尽? - jnnnnn
1
看起来你正在尝试将你的数字转换为18进制...为什么不直接使用(100308923948792839).toString(18)呢? - tewathia
tewathia,你是对的,但这仅适用于32位数字(我已修改示例)。 - jnnnnn
大多数编程语言将这种类型的函数称为“展开”。 - jnnnnn
1
我在研究这个主题时发现了这篇文章。可能是关于折叠和展开的有趣阅读... http://raganwald.com/2016/11/30/anamorphisms-in-javascript.html - user3798397
4个回答

4

// These days you can do it in one line:
const unfold = (accumulator, length) => length <= 0 ? accumulator : unfold([length, ...accumulator], length -1)

// invoke it like this:
const results = unfold([], 5)

// expected results:  1,2,3,4,5
console.log(results.join(','))

由于我们正在寻找一种简洁的方法来生成一个给定数量的元素作为数组,这个“unfold”函数使用递归实现。

第一个参数是累加器数组。这需要传递下去,并在它包含整个集合时返回。第二个参数是限制器。这是用于调整您的结果数组的维度。

在每次调用中,我们首先测试是否已达到基本情况。如果是,则答案很容易:只需返回给定的数组。对于一般情况,我们再次展开,但是使用较小的值,因此我们在累加器前面添加一个值,并减小长度。

由于我们使用了扩展运算符和计算if语句,所以这个函数非常简洁。使用箭头样式还可以避免使用"function"和"return"关键字,以及花括号。所以整个东西就是一行代码。

我基本上将这种技术用作React JSX的for循环替代品,其中每件事都需要成为表达式(Array.map())。


欢迎来到stackoverflow。请在代码中包含解释,并说明它如何解决问题。 - bad_coder

3

在Javascript中,Array#reduce的相反操作是Array.from(或者可以使用spread syntax)。您可以将其与任何可迭代对象一起使用以生成数组:

array = Array.from(iterator); // same as array = [...iterator];

你可以通过调用生成器函数来创建迭代器:

iterator = generate(params);

生成器函数使用特殊关键字 yield 返回它们的结果(或者使用 yield* 返回另一个可迭代对象的所有结果)。一旦返回,它们就被耗尽了:

function* convertBase(wet, alphabet) {
    const base = BigInt(alphabet.length);
    wet = BigInt(wet);
    while (wet > 0) {
        const digitVal = Number(wet % base);
        wet = wet / base;
        yield alphabet.charAt(digitVal);
    }
}

console.log(Array.from(convertBase(100308923948716816384684613592839, "0123456789ABCDEFGH")).reverse().join(""));

或者,您可以自己实现迭代器而不使用生成器函数:

console.log(Array.from({
    wet: BigInt(100308923948716816384684613592839),
    base: BigInt(18),
    alphabet: "0123456789ABCDEFGH",
    [Symbol.iterator]: function() {
        return this;
    },
    next: function() {
        if (this.wet > 0) {
            const digitVal = Number(this.wet % this.base);
            this.wet = this.wet / this.base;
            return {value: this.alphabet.charAt(digitVal)};
        } else {
            return {done: true};
        }
    }
}).reverse().join(""));


2

tewathia的评论似乎是最常用的做法,但如果你想走一条艰难的路,你可以自己编写递归原语,例如:

function unreduce(accumulator, operation, stopPredicate, ret) {
    return helper([accumulator, ret])[1]

    function helper(vals) {
        if (stopPredicate(vals[0])) return vals[1];

        return helper(operation(vals[0], vals[1]));
    }
}

您可能需要稍微修改一下,以保留回调函数中的this

我不确定这样做有多好。使用operation回调函数需要同时更新累加器和返回值,这有点棘手。外部函数无法避免operation必须返回一个长度为2的数组。


1
array.map((..) => { return [ .. ]; }).flat(1);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接