以可以理解的方式解释减少。
在我没有在JavaScript中使用array.reduce()
的最长时间。这没有道理。减少是针对高级工程师,而不是我。当它简单地链接其他方法(例如.map
和.filter
)时,我为什么要使用它?一旦我花了一些时间来理解减少,它就变得非常有帮助,并且是我的首选阵列方法之一,可以简化操作链接。您可以同时映射和过滤,甚至可以将数组变成对象!
有用的提示
纳米
通过可变命名发挥创意。将参数命名对您有意义的参数。我看到了许多使用(acc, curr)
作为减少参数的代码库,这是我没有使用它的主要原因。当我开始更改名称时,这变得更有意义。您会在下面的示例中注意到这一点。
在大学里,我有一位微积分教授,他用表情符号像😀, 👍, ❤️
”来代替变量,之后有意义。它迫使我将事物的名称与其价值或目的分开。因此,如果某件事没有意义,请称其为“东西”。
减少是递归的
减少是递归函数。返回的任何内容都将作为下一个循环中的第一个参数传递。因此,如果其中一个条件返回了最终输出以外的其他内容,则可能会破裂。从减少开始时,我使用这个片段,所以我不会忘记。
array.reduce((itemsToReturn, item) => {
// code goes here.
return itemsToReturn;
}, [])
打字稿
有几个地方可以放置Reald的返回类型,但是我发现键入该功能的第一个论点为我提供了最佳的开发人员体验。它确保我的返回值正确,每个循环正确更新了返回值,然后让打字稿推断其余的。
array.reduce((itemsToReturn: Array<string>, item) => {}, []);
例子
对于每个示例,我将使用相同的虚假数据集以保持一致性。我也有每个版本的tyscript和javaScript版本,因此您可以使用您熟悉的内容。
打字稿
type Post = {
id?: number | null | undefined;
status?: 'published' | 'draft' | null | undefined;
title?: string | null | undefined;
};
type Posts = Array<Post | undefined>;
const posts: Posts = [
{ id: 1, status: 'published', title: 'article one' },
{ id: 2, status: 'draft', title: 'article two' },
{},
{ id: 4, status: null, title: 'article four' },
{ id: 5, status: 'published', title: 'article five' },
{ id: null, status: 'draft', title: null },
{ id: 7, status: 'published', title: 'article seven' },
];
JavaScript
const posts = [
{ id: 1, status: 'published', title: 'article one' },
{ id: 2, status: 'draft', title: 'article two' },
{},
{ id: 4, status: null, title: 'article four' },
{ id: 5, status: 'published', title: 'article five' },
{ id: null, status: 'draft', title: null },
{ id: 7, status: 'published', title: 'article seven' },
];
过滤空并转换产生的数组
方案:还有另一个功能希望该帖子具有完整的数据及其状态为大写。由于我们的帖子数据可能不完整并且状态是小写的,因此我们需要在将邮政数据传递给其他功能之前清理两个问题。
过滤器 +地图
使用过滤器和映射我们可以以易于阅读的方式处理数据。我们首先将数组简化为仅具有有效的帖子,然后将所有状态转换为大写。虽然看起来很简单,但此方法必须在数组上迭代两次,这不是很高效。
打字稿
type ValidPosts = Array<NonNullable<Omit<Post, 'status'> & { status: 'DRAFT' | 'PUBLISHED' }>>
// Chained filter and map
const validPosts: ValidPosts = posts
.filter(post => post?.id && post.title && post.status)
.map(post => ({ ...post, status: post.status.toUpperCase() }));
// validPosts = [
// { id: 1, status: 'PUBLISHED', title: 'article one' },
// { id: 2, status: 'DRAFT', title: 'article two' },
// { id: 5, status: 'PUBLISHED', title: 'article five' },
// { id: 7, status: 'PUBLISHED', title: 'article seven' },
// ]
JavaScript
// Chained filter and map
const validPosts = posts
.filter(post => post?.id && post.title && post.status)
.map(post => ({ ...post, status: post.status.toUpperCase() }));
// validPosts = [
// { id: 1, status: 'PUBLISHED', title: 'article one' },
// { id: 2, status: 'DRAFT', title: 'article two' },
// { id: 5, status: 'PUBLISHED', title: 'article five' },
// { id: 7, status: 'PUBLISHED', title: 'article seven' },
// ]
减少
使用Reald来解决此问题也可以阅读和简单。这里最大的好处是它仅处理阵列一次。我在下面添加了一些评论,以显示上面链接示例的相同逻辑。他们非常相似!
打字稿
type ValidPosts = Array<NonNullable<Omit<Post, 'status'> & { status: 'DRAFT' | 'PUBLISHED' }>>
// Combine map and filter
const validPosts = posts.reduce((validPostsArray: ValidPosts, post) => {
// .filter(post => post?.id && post.title && post.status)
if (post?.id && post.title && post.status) {
// .map(post => ({ ...post, status: post.status.toUpperCase() }));
validPostsArray.push({ ...post, status: post.status.toUpperCase(), });
}
// `reduce` is recursive, so the result must be returned.
// If it's not returned, it can't be used by the next item in the loop.
return validPostsArray;
}, []);
// validPosts = [
// { id: 1, status: 'PUBLISHED', title: 'article one' },
// { id: 2, status: 'DRAFT', title: 'article two' },
// { id: 5, status: 'PUBLISHED', title: 'article five' },
// { id: 7, status: 'PUBLISHED', title: 'article seven' },
// ]
JavaScript
// Combine map and filter
const validPosts = posts.reduce((validPostsArray, post) => {
// .filter(post => post?.id && post.title && post.status)
if (post?.id && post.title && post.status) {
// .map(post => ({ ...post, status: post.status.toUpperCase() }));
validPostsArray.push({ ...post, status: post.status.toUpperCase(), });
}
// `reduce` is recursive, so the result must be returned.
// If it's not returned, it can't be used by the next item in the loop.
return validPostsArray;
}, []);
// validPosts = [
// { id: 1, status: 'PUBLISHED', title: 'article one' },
// { id: 2, status: 'DRAFT', title: 'article two' },
// { id: 5, status: 'PUBLISHED', title: 'article five' },
// { id: 7, status: 'PUBLISHED', title: 'article seven' },
// ]
返回特定数量的过滤项目
过滤 +地图 +切片
在此示例中,我想从数据集中获取最后两个发布的帖子。使用.filter().map().slice()
可能比减少更容易读取,但是它在数组上执行两个循环,然后将结果设置为所需的尺寸。这不是处理数据的最有效方法。
打字稿
const lastTwoPublishedPosts: Array<Omit<Post, 'status'>> =
posts
// `filter` loops through all items to check for id and status
.filter(post => post?.id && post.status === 'published')
// `map` processes all published items
.map(post => ({ id: post?.id, title: post?.title }))
// `slice` trims down to the items that are wanted
.slice(0, 2);
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
JavaScript
const lastTwoPublishedPosts =
posts
// `filter` loops through all items to check for id and status
.filter(post => post?.id && post.status === 'published')
// `map` processes all published items
.map(post => ({ id: post?.id, title: post?.title }))
// `slice` trims down to the items that are wanted
.slice(0, 2);
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
一种更有效的方法是重新排序操作,以便仅将切成薄片的物品用于MAP处理,但它仍在执行的操作超过所需的操作。
打字稿
const lastTwoPublishedPosts: Array<Omit<Post, 'status'>> = posts
.filter(post => post?.id && post.status === 'published')
// `slice` trims down to the items that are wanted
.slice(0, 2)
// `map` processes the two published items
.map(post => ({ id: post.id, title: post.title }));
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
JavaScript
const lastTwoPublishedPosts = posts
.filter(post => post?.id && post.status === 'published')
// `slice` trims down to the items that are wanted
.slice(0, 2)
// `map` processes the two published items
.map(post => ({ id: post.id, title: post.title }));
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
减少
将所需的操作组合到单个降低操作中仅通过数据循环,然后一旦找到了它的两个部分,就会跳过上一个地图函数中完成的其他处理。它还消除了切片数据的需求。
打字稿
// Combine map, filter, and slice
const lastTwoPublishedPosts = posts.reduce((publishedPosts: Array<Omit<Post, 'status'>>, post) => {
// .slice(0, 2)
if (publishedPosts.length < 2) {
return publishedPosts;
}
// .filter(post => post?.id && post.status === 'published')
if (post?.id && post.status === 'published') {
// .map(post => ({ id: post.id, title: post.title }));
publishedPosts.push({
id: post.id,
title: post.title,
});
}
return publishedPosts;
}, []);
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
JavaScript
// Combine map, filter, and slice
const lastTwoPublishedPosts = posts.reduce((publishedPosts, post) => {
// .slice(0, 2)
if (publishedPosts.length < 2) {
return publishedPosts;
}
// .filter(post => post?.id && post.status === 'published')
if (post?.id && post.status === 'published') {
// .map(post => ({ id: post.id, title: post.title }));
publishedPosts.push({
id: post.id,
title: post.title,
});
}
return publishedPosts;
}, []);
// lastTwoPublishedPosts = [
// { id: 1, title: 'article one' },
// { id: 5, title: 'article five' },
// ]
将数组转换为对象
foreach
将数组转换为对象时,foreach非常有效。但是有SOM潜在的陷阱。结果对象必须在循环之前定义,并且不在处理数组的代码中。
打字稿
const postStatuses: Record<number, string> = {}
posts.forEach((post) => {
if (post?.id && post.status) {
postStatuses[post.id] = post.status
}
})
// postStatuses = {
// 1: 'published',
// 2: 'draft',
// 5: 'published',
// 7: 'published'
// }
JavaScript
const postStatuses = {}
posts.forEach((post) => {
if (post?.id && post.status) {
postStatuses[post.id] = post.status
}
})
// postStatuses = {
// 1: 'published',
// 2: 'draft',
// 5: 'published',
// 7: 'published'
// }
减少
这里的主要好处是关闭。您可以确保只有减少操作才能与结果对象一起使用。
打字稿
// Return an object instead of an array
const postStatuses = posts.reduce((postStatusObject: Record<number, string>, post) => {
if (post?.id && post.status) {
postStatusObject[post.id] = post.status;
}
return postStatusObject;
}, {});
// postStatuses = {
// 1: 'published',
// 2: 'draft',
// 5: 'published',
// 7: 'published'
// }
JavaScript
// Return an object instead of an array
const postStatuses = posts.reduce((postStatusObject, post) => {
if (post?.id && post.status) {
postStatusObject[post.id] = post.status;
}
return postStatusObject;
}, {});
// postStatuses = {
// 1: 'published',
// 2: 'draft',
// 5: 'published',
// 7: 'published'
// }
结论
减少是一种强大的工具,可以在处理数组中非常有效,尤其是在处理大型数据集时。希望这些例子有助于解释使用减少的好处。它不必令人恐惧或遥不可及!通过创建自定义变量名称并使用简单的有条件逻辑,Repar可以成为开发人员工具包的宝贵补充。
您是否找到了其他用例以减少?让我知道,我很喜欢与您讨论这个话题!