转载: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. JSON.parseObject(String str)与JSONObject.parseObject(String str)的区别

    一.首先来说说fastjson fastjson 是一个性能很好的 Java 语言实现的 JSON 解析器和生成器,来自阿里巴巴的工程师开发.其主要特点是: ① 快速:fastjson采用独创的算法, ...

  2. ubuntu14的unity desktop显示异常

    在多用户下,卸载compiz后某个单一用户出现菜单栏和任务栏图标消失的情况. 这时,需要在图形界面下重置compiz $dconf reset -f /org/compiz/ 重启unity $set ...

  3. 解决pdf打印预览中遇到特殊字符,导出失败问题

    本博客是自己在学习和工作途中的积累与总结,仅供自己参考,也欢迎大家转载,转载时请注明出处. 由于近日由于pdf中存在特殊字符导致导出失败,主要原因是"&"字符与freema ...

  4. json字符串和Json对象,以及json的基本了解

    考虑到python等语言中没有更好表示json对象的方法,所以使用JavaScript来介绍json 首先是json字符串: var str1 = '{ "name": " ...

  5. HDU 5299 圆扫描线 + 树上删边

    几何+博弈的简单组合技 给出n个圆,有包含关系,以这个关系做游戏,每次操作可以选择把一个圆及它内部的圆全部删除,不能操作者输. 圆的包含关系显然可以看做是树型结构,所以也就是树上删边的游戏. 而找圆的 ...

  6. SQL语句(六)成批导入导出数据

    (六) 成批导入导出数据 假设已经存在teaching数据库, 存在一张Student表,如图: 右键teaching->任务->导入数据 下一步->数据源(Microsoft Ex ...

  7. javascript私有方法揭示为公有方法

    揭示模式可用于将私有方法暴露为公共方法.当为了对象的运转而将所有功能放置在一个对象中以及想尽可能地保护该对象,这种揭示模式就非常有用. 板栗: var myarray; (function(){ va ...

  8. javascript类式继承函数最优版

    直接上代码: klass函数 var klass = function (Parent, props) { var Child, F, i; //1.新构造函数 Child = function () ...

  9. C 语言结构体之点运算符( . )和箭头运算符( -> )的区别

    很多时候,在对结构体进行相应的编码时,时而发现是用点运算符( . ),时而是用箭头运算符( -> ):那么这两者之间的使用有什么区别么? 相同点:两者都是二元操作符,而且右边的操作数都是成员的名 ...

  10. 广度优先搜索(BFS)----------------(TjuOj1140_Dungeon Master)

    这次整理了一下广度优先搜索的框架,以后可以拿来直接用了.TjuOj1140是一个三维的迷宫题,在BFS时我增加了一个控制数组,用来对队列的出队进行控制,确保每次出队的结点均为同一步长的结点,个人认为比 ...