JavaScript 同时进行地图和查找:findMap?
JavaScript map & find at the same time: 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和下划线中都没有找到。
像这样
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));
我遵循的方法是将调用'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));
您可以使用 .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);
内置 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
是一个生成器,returns(产量)按需生成结果,而不是一次处理整个数组。本例中的 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
为什么不使用更智能的查找功能?
let desiredValue;
const result = a.find( x =>{
desiredValue = expensiveFunction(x);
return desiredValue > 100;
});
console.log(desiredValue);
它会在找到第一个结果后立即退出昂贵的循环。
如果您愿意接受数组中的第一个匹配元素被修改,您可以这样做:
a[a.findIndex((value, index) => {
value = expensiveFunction(value);
return (value > 100 && (a[index] = value))
})] //Returns 156
否则,您将需要使用占位符变量来完成此工作 - 很可能使 for 循环成为最干净的选项。
您可以通过使用三元运算符简化条件并使用 filter() 删除数组的布尔值(空)来完成最短的方法。
const a = [2, 5, 78, 100];
const result = a.map((n)=> 2*n > 100 ? 2*n : null ).filter(Boolean)[0];
console.log(result);
这是 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)
您可以对一个可迭代对象执行多个操作,而只对值进行一次迭代。
下面是在 iter-ops 库的帮助下执行此操作的示例:
import {pipe, map, start} from 'iter-ops';
const a = [2, 5, 78, 4];
const res = pipe(
a,
map(m => 2 * m), // your expensive function
start(r => r > 100)
);
console.log('result:', res.first);
在上面,它将执行与您的 for-loop
示例中相同数量的步骤。
如果不使用 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和下划线中都没有找到。
像这样
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));
我遵循的方法是将调用'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));
您可以使用 .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);
内置 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
是一个生成器,returns(产量)按需生成结果,而不是一次处理整个数组。本例中的 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
为什么不使用更智能的查找功能?
let desiredValue;
const result = a.find( x =>{
desiredValue = expensiveFunction(x);
return desiredValue > 100;
});
console.log(desiredValue);
它会在找到第一个结果后立即退出昂贵的循环。
如果您愿意接受数组中的第一个匹配元素被修改,您可以这样做:
a[a.findIndex((value, index) => {
value = expensiveFunction(value);
return (value > 100 && (a[index] = value))
})] //Returns 156
否则,您将需要使用占位符变量来完成此工作 - 很可能使 for 循环成为最干净的选项。
您可以通过使用三元运算符简化条件并使用 filter() 删除数组的布尔值(空)来完成最短的方法。
const a = [2, 5, 78, 100];
const result = a.map((n)=> 2*n > 100 ? 2*n : null ).filter(Boolean)[0];
console.log(result);
这是 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)
您可以对一个可迭代对象执行多个操作,而只对值进行一次迭代。
下面是在 iter-ops 库的帮助下执行此操作的示例:
import {pipe, map, start} from 'iter-ops';
const a = [2, 5, 78, 4];
const res = pipe(
a,
map(m => 2 * m), // your expensive function
start(r => r > 100)
);
console.log('result:', res.first);
在上面,它将执行与您的 for-loop
示例中相同数量的步骤。