如何 "pass" 链式映射和过滤器之间的额外数据
How to "pass" extra data between chained maps and filters
这主要是出于学术兴趣,因为我设法以一种完全不同的方式解决了它,但是,短篇小说,我想要的是伪代码:
Foreach object in array1
Find matching otherObject in array2 // must exist and there's only 1
Find matching record in array3 // must exist and there's only 1
If record.status !== otherObject.status
push { otherObject.id, record.status } onto newArray
在我看来,直觉上似乎应该有一种方法可以用 array1.filter(<some function>).map(<some other function>
做一些事情,但我无法在实践中使用它。
这是一个真实的例子。这有效:
function update(records) {
const filtered = records.filter((mcr) => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
return (match.subscriberStatus.toLowerCase() !==
mc.mailingList.find((listEntry) =>
listEntry.id === mcr.id).status.toLowerCase()
);
});
const toUpdate = filtered.map((mcr) => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
return ({ 'id': match.id,
'fields': {'Mailing List Status': mcr.subscriberStatus }
}
);
});
}
但让我恼火的是重复的 const match =
。在我看来,如果 at._people
是一个大数组,这些可能会很昂贵。
我天真地尝试过:
function update(records) {
let match;
const toUpdate = records.filter((mcr) => {
match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
// return isDifferent?
return (match.subscriberStatus.toLowerCase() !==
mc.mailingList.find((listEntry) => listEntry.id === mcr.id).status.toLowerCase());
}).map((foundMcr) => {
return ({ 'id': match.id, 'fields': {'Mailing List Status': foundMcr.subscriberStatus } })
});
}
但是(回想起来有点明显)这是行不通的,因为在 map
中,match
永远不会改变 — 它总是在 [=19= 中的最后一件事].关于如何将在 filter
中找到的 match.id
逐项传递给链式 map
的任何想法?或者,真的,还有其他方法可以完成同样的事情吗?
如果这对您有帮助,我会这样做:
let newArray = array1.filter((item) => {
let matching1 = array2.filter(matchingFunction)
let matching2 = array3.filter(matchingFunction)
return matching1?.status == matching2?.status;
})
如果您 仅 使用 .map
和 .filter()
那么您可以避免稍后在链中进行额外的重新计算,如果您执行以下操作(一般步骤):
.map()
将每个项目放入包含的包装器对象中:
- 数组中的项目
- 计算您在后续步骤中需要的额外数据
.filter()
基于计算数据的包装器对象。
.map()
剩余的结果变成你想要的形状,在原始项目和任何计算数据上绘制。
在您的情况下,这可能意味着:
- 您执行一次查找逻辑。
- 使用找到的项目丢弃一些结果。
- 使用剩余的内容生成一个新数组。
下面是提取回调的结果,使 map/filter/map 逻辑更清晰:
//takes a record and enriches it with `match` and `mailingStatus`
const wrapWithLookups = mcr => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
const mailingListStatus = mc.mailingList.find((listEntry) => listEntry.id === mcr.id).status;
return { match, mailingListStatus , mcr };
};
//filters based on match and mailingListStatus calculated fields
const isCorrectSubscriberStatus = ({match, mailingListStatus}) =>
match.subscriberStatus.toLowerCase() !== mailingListStatus .toLowerCase();
//converts to a new item based on mcr and match
const toUpdatedRecord = ({match, mcr}) => ({
'id': match.id,
'fields': {'Mailing List Status': mcr.subscriberStatus }
});
function update(records) {
return records
.map(wrapWithLookups)
.filter(isCorrectSubscriberStatus)
.map(toUpdatedRecord);
}
如果以后需要,这将节省 match
and/or mailingStatus
的重新计算。但是,它确实在数组中引入了一个全新的循环来收集它们。这可能是一个性能问题,但是,如果您像 Lodash 提供的那样使用惰性求值链,则很容易补救。使用的代码调整为:
function update(records) {
return _(records) // wrap in a lazy chain evaluator by Lodash ->-+
.map(wrap) // same as before |
.filter(isCorrectSubscriberStatus) // same as before |
.map(toUpdatedRecord) // same as before |
.value(); // extract the value <-----------------------------+
}
其他图书馆可能采用非常相似的方法。在任何情况下,延迟评估不会 运行 一次通过数组 .map()
,然后另一次 .filter()
,然后第三次第二次 .map()
而是仅迭代一次和 运行s 适当的操作。
惰性评估可以通过构建在 reduce()
之上的转换器来表达。有关传感器工作原理的示例,请参见:
这样就可以避免所有的.map()
和.filter()
的调用,只需做一个组合函数,直接使用.reduce()
。但是,我个人发现,如果需要性能,与通过 .map().filter().map()
链表达逻辑然后使用惰性求值相比,更难推理也更难维护。
值得注意的是 map()
-> filter()
-> map()
逻辑不需要通过惰性链使用。您可以使用像 FP distribution of Lodash or the vanilla Ramda 这样的库,它为您提供通用的 map()
和 filter()
函数,这些函数可以应用于任何列表并相互组合以避免再次重复多次。使用 Lodash FP 这将是:
import map from 'lodash/fp/map';
import filter from 'lodash/fp/filter';
import flow from 'lodash/fp/flow';
function update(records) {
const process = flow(
map(wrapWithLookups),
filter(isCorrectSubscriberStatus),
map(toUpdatedRecord),
);
return process(records);
}
对于 Ramda,实现是相同的——map()
和 filter()
在两个库中的行为相同,唯一的区别是组合函数(Ramda 中的 flow()
in Lodash) is called pipe
。它的作用与 flow()
:
相同
pipe(
map(wrapWithLookups),
filter(isCorrectSubscriberStatus),
map(toUpdatedRecord),
)
要更深入地了解为什么您可能希望避免链接和此处的替代方法,请参阅有关 Medium.com 的文章:Why using _.chain
is a mistake.
这主要是出于学术兴趣,因为我设法以一种完全不同的方式解决了它,但是,短篇小说,我想要的是伪代码:
Foreach object in array1
Find matching otherObject in array2 // must exist and there's only 1
Find matching record in array3 // must exist and there's only 1
If record.status !== otherObject.status
push { otherObject.id, record.status } onto newArray
在我看来,直觉上似乎应该有一种方法可以用 array1.filter(<some function>).map(<some other function>
做一些事情,但我无法在实践中使用它。
这是一个真实的例子。这有效:
function update(records) {
const filtered = records.filter((mcr) => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
return (match.subscriberStatus.toLowerCase() !==
mc.mailingList.find((listEntry) =>
listEntry.id === mcr.id).status.toLowerCase()
);
});
const toUpdate = filtered.map((mcr) => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
return ({ 'id': match.id,
'fields': {'Mailing List Status': mcr.subscriberStatus }
}
);
});
}
但让我恼火的是重复的 const match =
。在我看来,如果 at._people
是一个大数组,这些可能会很昂贵。
我天真地尝试过:
function update(records) {
let match;
const toUpdate = records.filter((mcr) => {
match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
// return isDifferent?
return (match.subscriberStatus.toLowerCase() !==
mc.mailingList.find((listEntry) => listEntry.id === mcr.id).status.toLowerCase());
}).map((foundMcr) => {
return ({ 'id': match.id, 'fields': {'Mailing List Status': foundMcr.subscriberStatus } })
});
}
但是(回想起来有点明显)这是行不通的,因为在 map
中,match
永远不会改变 — 它总是在 [=19= 中的最后一件事].关于如何将在 filter
中找到的 match.id
逐项传递给链式 map
的任何想法?或者,真的,还有其他方法可以完成同样的事情吗?
如果这对您有帮助,我会这样做:
let newArray = array1.filter((item) => {
let matching1 = array2.filter(matchingFunction)
let matching2 = array3.filter(matchingFunction)
return matching1?.status == matching2?.status;
})
如果您 仅 使用 .map
和 .filter()
那么您可以避免稍后在链中进行额外的重新计算,如果您执行以下操作(一般步骤):
.map()
将每个项目放入包含的包装器对象中:- 数组中的项目
- 计算您在后续步骤中需要的额外数据
.filter()
基于计算数据的包装器对象。.map()
剩余的结果变成你想要的形状,在原始项目和任何计算数据上绘制。
在您的情况下,这可能意味着:
- 您执行一次查找逻辑。
- 使用找到的项目丢弃一些结果。
- 使用剩余的内容生成一个新数组。
下面是提取回调的结果,使 map/filter/map 逻辑更清晰:
//takes a record and enriches it with `match` and `mailingStatus`
const wrapWithLookups = mcr => {
const match = at._people.find((atr) => atr.email.toLowerCase() ===
mcr.email.toLowerCase());
const mailingListStatus = mc.mailingList.find((listEntry) => listEntry.id === mcr.id).status;
return { match, mailingListStatus , mcr };
};
//filters based on match and mailingListStatus calculated fields
const isCorrectSubscriberStatus = ({match, mailingListStatus}) =>
match.subscriberStatus.toLowerCase() !== mailingListStatus .toLowerCase();
//converts to a new item based on mcr and match
const toUpdatedRecord = ({match, mcr}) => ({
'id': match.id,
'fields': {'Mailing List Status': mcr.subscriberStatus }
});
function update(records) {
return records
.map(wrapWithLookups)
.filter(isCorrectSubscriberStatus)
.map(toUpdatedRecord);
}
如果以后需要,这将节省 match
and/or mailingStatus
的重新计算。但是,它确实在数组中引入了一个全新的循环来收集它们。这可能是一个性能问题,但是,如果您像 Lodash 提供的那样使用惰性求值链,则很容易补救。使用的代码调整为:
function update(records) {
return _(records) // wrap in a lazy chain evaluator by Lodash ->-+
.map(wrap) // same as before |
.filter(isCorrectSubscriberStatus) // same as before |
.map(toUpdatedRecord) // same as before |
.value(); // extract the value <-----------------------------+
}
其他图书馆可能采用非常相似的方法。在任何情况下,延迟评估不会 运行 一次通过数组 .map()
,然后另一次 .filter()
,然后第三次第二次 .map()
而是仅迭代一次和 运行s 适当的操作。
惰性评估可以通过构建在 reduce()
之上的转换器来表达。有关传感器工作原理的示例,请参见:
这样就可以避免所有的.map()
和.filter()
的调用,只需做一个组合函数,直接使用.reduce()
。但是,我个人发现,如果需要性能,与通过 .map().filter().map()
链表达逻辑然后使用惰性求值相比,更难推理也更难维护。
值得注意的是 map()
-> filter()
-> map()
逻辑不需要通过惰性链使用。您可以使用像 FP distribution of Lodash or the vanilla Ramda 这样的库,它为您提供通用的 map()
和 filter()
函数,这些函数可以应用于任何列表并相互组合以避免再次重复多次。使用 Lodash FP 这将是:
import map from 'lodash/fp/map';
import filter from 'lodash/fp/filter';
import flow from 'lodash/fp/flow';
function update(records) {
const process = flow(
map(wrapWithLookups),
filter(isCorrectSubscriberStatus),
map(toUpdatedRecord),
);
return process(records);
}
对于 Ramda,实现是相同的——map()
和 filter()
在两个库中的行为相同,唯一的区别是组合函数(Ramda 中的 flow()
in Lodash) is called pipe
。它的作用与 flow()
:
pipe(
map(wrapWithLookups),
filter(isCorrectSubscriberStatus),
map(toUpdatedRecord),
)
要更深入地了解为什么您可能希望避免链接和此处的替代方法,请参阅有关 Medium.com 的文章:Why using _.chain
is a mistake.