转载:https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

Using mapfilter or reduce to operate on Swift collection types such as Array or Dictionary is something that can take getting used to. Unless you have experience with functional languages your instinct may be to reach for the more familiar for-in loop. With that in mind here is my guide to using map, filter, reduce (and flatMap).

Map

Use map to loop over a collection and apply the same operation to each element in the collection. The map function returns an array containing the results of applying a mapping or transform function to each item:

We could use a for-in loop to compute the squares of each item in an array:

let values = [2.0,4.0,5.0,7.0]
var squares: [Double] = []
for value in values {
  squares.append(value*value)
}

This works but the boilerplate code to declare the type of the squares array and then loop over it is a little verbose. We also need to make the squaresarray a var as we are changing it in the loop. Now compare to when we use map:

let values = [2.0,4.0,5.0,7.0]
let squares = values.map {$0 * $0}
// [4.0, 16.0, 25.0, 49.0]

This is a big improvement. We don’t need the for loop as map takes care of that for us. Also the squares result is now a let or non-mutating value and we did not even need to declare its type as Swift can infer it.

The shorthand closure syntax can make this hard to follow at first. The map function has a single argument which is a closure (a function) that it calls as it loops over the collection. This closure takes the element from the collection as an argument and returns a result. The map function returns these results in an array.

Writing the mapping function in long form can make it easier to see what is happening:

let values = [2.0,4.0,5.0,7.0]
let squares2 = values.map({
  (value: Double) -> Double in
  return value * value
})

The closure has a single argument: (value: Double) and returns a Doublebut Swift can infer this. Also since map has a single argument which is a closure we do not need the ( and ) and with a single line closure we can even omit the return:

let squares2 = values.map {value in value * value}

The in keyword separates the argument from the body of the closure. If you prefer you can go one step further and use the numbered arguments shorthand:

let squares = values.map { $0 * $0 }

The type of the results is not limited to the type of the elements in the original array. Here is an example of mapping an array of integers to strings:

let scores = [0,28,124]
let words = scores.map { NSNumberFormatter.localizedStringFromNumber($0,
    numberStyle: .SpellOutStyle) }
// ["zero", "twenty-eight", "one hundred twenty-four"]

The map operation is not limited to Arrays you can use it anywhere you have a collection type. For example, use it with a Dictionary or a Set, the result will always be an Array. Here is an example with a Dictionary:

let milesToPoint = ["point1":120.0,"point2":50.0,"point3":70.0]
let kmToPoint = milesToPoint.map { name,miles in miles * 1.6093 }

Quick tip: If you have trouble understanding the argument types of the closure Xcode code completion will help you:

In this case we are mapping a Dictionary so as we iterate over the collection our closure has arguments that are a String and a Double from the types of the key and value that make up each element of the dictionary.

A final example with a Set:

let lengthInMeters: Set = [4.0,6.2,8.9]
let lengthInFeet = lengthInMeters.map {meters in meters * 3.2808}

In this case we have a set containing elements of type Double so our closure also expects a Double.

Filter

Use filter to loop over a collection and return an Array containing only those elements that match an include condition.

The filter method has a single argument that specifies the include condition. This is a closure that takes as an argument the element from the collection and must return a Bool indicating if the item should be included in the result.

An example that filters as array of integers returning only the even values:

let digits = [1,4,10,15]
let even = digits.filter { $0 % 2 == 0 }
// [4, 10]

Reduce

Use reduce to combine all items in a collection to create a single new value.

The reduce method takes two values, an initial value and a combine closure. For example, to add the values of an array to an initial value of 10.0:

let items = [2.0,4.0,5.0,7.0]
let total = items.reduce(10.0,combine: +)
// 28.0

This will also work with strings using the + operator to concatenate:

let codes = ["abc","def","ghi"]
let text = codes.reduce("", combine: +)
// "abcdefghi"

The combine argument is a closure so you can also write reduce using the trailing closure syntax:

let names = ["alan","brian","charlie"]
let csv = names.reduce("===") {text, name in "\(text),\(name)"}
// "===,alan,brian,charlie"

FlatMap

The simplest use is as the name suggests to flatten a collection of collections.

let collections = [[5,2,7],[4,8],[9,1,3]]
let flat = collections.flatMap { $0 }
// [5, 2, 7, 4, 8, 9, 1, 3]

Even more usefully it knows about optionals and will remove them from a collection.

let people: [String?] = ["Tom",nil,"Peter",nil,"Harry"]
let valid = people.flatMap {$0}
// ["Tom", "Peter", "Harry"]

The real power of flatMap comes when you use it to produce an Arraywhich is the flattened concatenation of transforming each of the subarrays.

For example to return an array of even integers contained in a collection of integer arrays by applying a filter to each item in the subarrays:

let collections = [[5,2,7],[4,8],[9,1,3]]
let onlyEven = collections.flatMap {
  intArray in intArray.filter { $0 % 2 == 0 }
}
// [2, 4, 8]

Note that flatMap is iterating over the subarrays of integers so its argument is a closure whose argument intArray is of type [Int]. This is also a situation where I find the shorthand closure syntax hard to read but you could write this:

let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } }

Another example to produce a flat Array that contains the squares of each Int by applying a map to each subarray and then concatenating the result:

let allSquared = collections.flatMap { $0.map { $0 * $0 } }

or in longer form:

let allSquared = collections.flatMap {
  intArray in intArray.map { $0 * $0 }
}
// [25, 4, 49, 16, 64, 81, 1, 9]

A final example that returns the individual sums of each of the arrays of integers by applying reduce to each of the subarrays:

let sums = collections.flatMap { $0.reduce(0, combine: +) }

Note though as someone helpfully pointed out to me this last example can be achieved with a plain map as reduce is returning an integer not an array:

let sums = collections.map { $0.reduce(0, combine: +) }

Chaining

You can chain methods. For example to sum only those numbers greater than or equal to seven we can first filter and then reduce:

let marks = [4,5,8,2,9,7]
let totalPass = marks.filter{$0 >= 7}.reduce(0,combine: +)
// 24

Another example that returns only the even squares by first mapping and then filtering:

let numbers = [20,17,35,4,12]
let evenSquares = numbers.map{$0 * $0}.filter{$0 % 2 == 0}
// [400, 16, 144]

Quick Summary

Next time you find yourself looping over a collection check if you could use map, filter or reduce:

  • map returns an Array containing results of applying a transform to each item.
  • filter returns an Array containing only those items that match an include condition.
  • reduce returns a single value calculated by calling a combine closure for each item with an initial value.

Swift map filter reduce 使用指南的更多相关文章

  1. python 内置函数 map filter reduce lambda

    map(函数名,可遍历迭代的对象) # 列组元素全加 10 # map(需要做什么的函数,遍历迭代对象)函数 map()遍历序列得到一个列表,列表的序号和个数和原来一样 l = [2,3,4,5,6, ...

  2. 如何在python3.3用 map filter reduce

    在3.3里,如果直接使用map(), filter(), reduce(), 会出现 >>> def f(x): return x % 2 != 0 and x % 3 != 0  ...

  3. python常用函数进阶(2)之map,filter,reduce,zip

    Basic Python : Map, Filter, Reduce, Zip 1-Map() 1.1 Syntax # fun : a function applying to the iterab ...

  4. 数组的高阶方法map filter reduce的使用

    数组中常用的高阶方法: foreach    map    filter    reduce    some    every 在这些方法中都是对数组中每一个元素进行遍历操作,只有foreach是没有 ...

  5. lambda,map,filter,reduce

    lambda 编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数.返回一个函数对象. func = lambda x,y:x+y fu ...

  6. Python map filter reduce enumerate zip 的用法

    map map(func, list) 把list中的数字,一个一个运用到func中,常和lambda一起用. nums = [1, 2, 3, 4, 5] [*map(lambda x: x**2, ...

  7. Python中map,filter,reduce,zip的应用

    事例1: l=[('main', 'router_115.236.xx.xx', [{'abc': 1}, {'dfg': 1}]), ('main', 'router_183.61.xx.xx', ...

  8. python几个特别函数map filter reduce lambda

    lambda函数也叫匿名函数,即,函数没有具体的名称.先来看一个最简单例子: def f(x): return x**2 print f(4) Python中使用lambda的话,写成这样 g = l ...

  9. javascript数组详解(js数组深度解析)【forEach(),every(),map(),filter(),reduce()】

    Array 对象是一个复合类型,用于在单个的变量中存储多个值,每个值类型可以不同. 创建数组对象的方法: new Array(); new Array(size); new Array(element ...

随机推荐

  1. Hadoop生态圈-Kafka的本地模式部署

    Hadoop生态圈-Kafka的本地模式部署 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Kafka简介 1>.什么是JMS 答:在Java中有一个角消息系统的东西,我 ...

  2. 【DS】排序算法之归并排序(Merge Sort)

    一.算法思想 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法的一个非常典型的应用,指的是将两个已经排序的序列合并成一个序列的操作.其归并思想如下: 1)申请空间,使其大小为两个已经 ...

  3. SCI写作经典替换词

  4. 《翻译》PEP 380 – 委托子生成器语法

     PEP 380 – 委托子生成器语法 翻译自: https://www.python.org/dev/peps/pep-0380/ 摘要 一项新的语法被提出了:生成器委托其部分操作给另一个生成器.委 ...

  5. 公告:关注canvas的同学注意了

    因为我之前把基础大致都帮各位详细讲过了! 什么fill,line,乱七八糟的一堆.都有demo了 所以我最近写起来可能会快很多了!如果有不明白的只能请各位回顾下之前的文章了 毕竟如果按照这个进度写文章 ...

  6. ASP.net学习总结

    学习ASP.net又一次接触了B/S开发.下面先通过一张图对ASP.net有一个宏观结构的总结.之后将详细介绍ASP.net中的六大对象. 1.Request从客户端得到数据,包括基于表单的数据和通过 ...

  7. sqlserver2008R2数据库自动备份脚本

    CREATE proc [dbo].[usp_autoBackupDB] @dbname sysname=null --要备份的数据库名,不指定即为全部备份 ,)='d:\' --备份目录路径 ,)= ...

  8. Java内存模型-final域的内存语义

    一 引言 说到final你肯定知道它是Java中的关键字,那么它所在Java中的作用你知道吗?不知道的话,请前往这篇了解下https://www.cnblogs.com/yuanfy008/p/802 ...

  9. orm 缺点

    背景 提起orm,在我开发这几年可是阴魂不散,因为我的开发没人带,全是自己琢磨,好处是很多东西都懂,都理解的透彻,缺点是见得少,接触少.而我一直没用orm,但是又到处听说orm,但我总想不明白有啥用处 ...

  10. Python api认证

    本节内容: 基本的api 升级的api 终极版api 环境:Djanao, 项目名:api_auto, app:api 角色:api端,客户端,黑客端 1.基本的api [api端] #api_aut ...