首页 > 解决方案 > JavaScript 同时映射和查找:findMap?

问题描述

如果不使用 for 循环,你将如何重写它?

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;

let result;

// Find the first number 
for (let i = 0; i < a.length; i++) {
    const r = expensiveFunction(a[i]);

    if (r > 100) {
        result = r;
        break;
    }
}

console.log(result);

我天真的方法:

const result = a.map(expensiveFunction).find(x => x > 100);
console.log(result);

但这expensiveFunction适用于我想避免的所有元素。在上述情况下,我们应该避免运行expensiveFunction(4).

有些语言有find_map(例如Rust),我没有在 lodash 和下划线中找到它。

标签: javascriptlazy-evaluation

解决方案


内置map是贪婪的,所以你必须编写自己的懒惰版本:

const a = [2, 5, 78, 4];
const expensiveFunction = n => {
     console.log('expensiveFunction for', n); 
     return 2 * n 
};


function *map(a, fn) {
    for(let x of a)
        yield fn(x);
}

function find(a, fn) {
    for(let x of a)
        if (fn(x))
            return x;
}



r = find(map(a, expensiveFunction), x => x > 100)
console.log('result', r)

与 stock 不同map,这map是一个生成器,并根据需要返回(生成)结果,而不是一次处理整个数组。find在这个例子中map是“协程”并玩某种乒乓球游戏,find要求结果并在被要求map时提供结果。一旦find对它得到的东西感到满意,它就会退出map,因为没有人再要求它的结果了。

您还可以将map,find和朋友添加到IteratorPrototype以使它们可用于所有迭代器并能够使用点表示法:

const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));

Object.defineProperties(IteratorPrototype, {
    map: {
        value: function* (fn) {
            for (let x of this) {
                yield fn(x);
            }
        },
        enumerable: false
    },

    find: {
        value: function (fn) {
            for (let x of this) {
                if (fn(x))
                    return x;
            }
        },
        enumerable: false
    },

});

//

const a = [2, 5, 78, 4];
const expensiveFunction = n => {
    console.log('expensiveFunction', n);
    return 2 * n
};


let r = a.values().map(expensiveFunction).find(x => x > 100);

console.log(r)

这是一个基于这种技术的小型库:https ://github.com/gebrkn/armita


推荐阅读