JavaScript地图并同时查找:findMap?

时间:2019-09-02 16:18:18

标签: javascript lazy-evaluation

如何在不使用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和下划线中都找不到。

8 个答案:

答案 0 :(得分:5)

内置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)

与股票map不同,此map是生成器,可按需返回(收益)结果,而不是立即处理整个数组。在此示例中,findmap是“协程”,它们玩某种类型的乒乓球游戏,其中find要求结果,map则根据要求提供结果。 find对所获得的结果一满意,它就会退出,map也将退出,因为没有人再要求它的结果了。

您还可以将mapfind和朋友添加到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

答案 1 :(得分:3)

类似这样

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;
let findMap = arr => {
  let found = arr.find(a => expensiveFunction(a) > 100)
  return found !== undefined ? expensiveFunction(found) : found
}

console.log(findMap(a));


警告:-只是出于好奇,但是很容易受到攻击,或者您可以称其为滥用find

const a = [2, 5, 78, 4];
const expensiveFunction = n => 2 * n;
let findMap = arr => {
  let returnValue;
  let found = arr.find(a => {
    returnValue = expensiveFunction(a)
    return returnValue > 100
  })
  return returnValue
}

console.log(findMap(a));

答案 2 :(得分:2)

如果您愿意接受修改数组中的第一个匹配元素,则可以执行以下操作:

a[a.findIndex((value, index) => {
    value = expensiveFunction(value); 
    return (value > 100 && (a[index] = value))
})] //Returns 156

否则,您将需要使用占位符变量来完成这项工作-很有可能使for循环成为最干净的选项。

答案 3 :(得分:1)

您可以使用.reduce,唯一的缺点是一旦找到一个值就无法停止,但您不必为每个值运行expensiveFunction

这里是一个例子:

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

const result = a.reduce((acc, cur) => {
  if (!acc) {
    const r = expensiveFunction(cur);
    if (r > 100) {
      acc = r;
    }
  }
  return acc;
}, null);



console.log(result);

答案 4 :(得分:1)

您可以通过使用三元运算符简化条件,并使用filter()删除Array的Boolean(null)值的最短方法。

const a = [2, 5, 78, 100];
const result  = a.map((n)=>  2*n > 100 ? 2*n : null ).filter(Boolean)[0];
console.log(result);

答案 5 :(得分:0)

我遵循的方法是将调用“ expensiveFunction”函数的可能性降低到最小。为此,我使用了“ 分而治之算法”。您将数组分为两半,并在除法元素上调用昂贵的函数来确定要进行哪一半。递归执行此步骤,直到找到最小的元素大于100。特别是对于非常大的数组,此方法会将昂贵的函数调用减少到明显更少的数目。因此,“ expensiveFunCaller”函数将经济地调用您的“ expensiveFunction”。数组也应该首先排序。

const a = [2, 5,78, 80].sort((a,b) => a-b);
const expensiveFunction = n => 2 * n;

const expensiveFunCaller= ([...arr]) =>{
  if(arr.length<2){
    let r = expensiveFunction(arr[0]);
    if(r>100) return r;
    return;
  }
  else if(arr.length === 2){
    let r = expensiveFunction(arr[0]);
    if(r>100) return r;
    r = expensiveFunction(arr[1]);
    if(r>100) return r;
    return;
  }
  let idx = Math.floor(arr.length / 2);
  let r = expensiveFunction(arr[idx]);

  return (r<100)?expensiveFunCaller(arr.slice(idx+1, arr.length)):expensiveFunCaller(arr.slice(0, idx+1));
}
console.log(expensiveFunCaller(a));

答案 6 :(得分:0)

为什么不使用更智能的功能进行查找?

let desiredValue;
const result = a.find( x =>{
      desiredValue = expensiveFunction(x);
      return desiredValue > 100;
});
log( desiredValue );

找到第一个结果后,它将立即退出昂贵的循环。

答案 7 :(得分:0)

这是 Titus .reduce 答案的简洁实用版本:

const arr = [2, 5, 78, 100]
const result = arr.reduce((a,v) => (a > 100 && a) || expensiveFunction(v), null)
console.log(result)

它遍历整个数组,但一旦满足条件就停止执行昂贵的函数。

以下是我个人使用的,以防它对任何人有帮助:

const result = arr.reduce((a,v) => a || expensiveFunction(v), null)