LoginSignup
31
24

More than 5 years have passed since last update.

Ruby と Scala と Swift と JavaScript における map と flatMap

Last updated at Posted at 2015-05-30

Ruby, Scala, Swift, JavaScript での List や Array といったデータ型の map と flatMap について[1, 2, 3, [4, [5, [6]]]]という何気ないデータを使って動きを見てみます。

map

mapの動作イメージは、箱から出した値を変換する関数に渡して変換し、その結果をまた箱に詰める感じだと思っています。

map.png

元のデータがリストの場合は、すべての要素が関数に渡され、それぞれ変換された値が入った新しい箱ができます。

Ruby

[1] pry(main)> [1, 2, 3, [4, [5, [6]]]].map {|x| p x; x }
1                              # 1つ目の要素
2                              # 2つめの要素
3                              # 3つめの要素
[4, [5, [6]]]                  # 4つめの要素
=> [1, 2, 3, [4, [5, [6]]]]    # 新しいリスト

Scala

scala> List(1, 2, 3, List(4, List(5, List(6)))).map { (x) => println(x); x }
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
List(4, List(5, List(6)))      # 4つ目の要素
res1: List[Any] = List(1, 2, 3, List(4, List(5, List(6))))    # 新しいリスト

Swift

println(([1, 2, 3, [4, [5, [6]]]] as [AnyObject]).map { i -> AnyObject in println(i); return i; })
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
(                              # ---
    4,                         #  |
        (                      #  |
        5,                     #  |
                (              #  | 4つ目の要素
            6                  #  |
        )                      #  |
    )                          #  |
)                              # ---
[1, 2, 3, (                    # ---
    4,                         #  |
        (                      #  |
        5,                     #  |
                (              #  | 新しいリスト
            6                  #  |
        )                      #  |
    )                          #  |
)]                             # ---

JavaScript

> [1, 2, 3, [4, [5, [6]]]].map(function(x){ console.log(x); return x; })
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
[ 4, [ 5, [ 6 ] ] ]            # 4つ目の要素
[ 1,                           # ---
  2,                           #  | 新しいリスト
  3,                           #  |
  [ 4, [ 5, [Object] ] ] ]     # ---

まとめ

どの言語も全部同じ挙動。

flatMap

flatMapはmapしてflattenする、ただのflattenとmapの合成関数だと思っています。
つまりこういうものなんじゃないかと→flatMap(f) = flatten(map(f))

flatmap1.png

こうやって書くとmapと同じに見えますが、大きな違いは「変換!」の関数の戻り値で、mapの場合は値を返しますが、flatMapの場合は箱を返す必要があります。
せっかく合成した関数ですが、わかりやすいようにバラバラにするとこんな感じになります。

flatmap2.png

これだけだとflatMapの存在意義が感じられないのですが、箱で返すので実際にはいろいろ利用用途があります。値をいじるだけならmapでできますが、要素を増やしたり減らしたりするときはflatMapを使います。
例えば[値] => [値, -値]という変換ができます。

flatmap3.png

Ruby

[1] pry(main)> [1, 2, 3, [4, [5, [6]]]].flat_map {|x| p x; [x, x] }
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
[4, [5, [6]]]                  # 4つ目の要素
=> [1, 1, 2, 2, 3, 3, [4, [5, [6]]], [4, [5, [6]]]]    # 新しいリスト

mapしてflattenすると、

[2] pry(main)> [1, 2, 3, [4, [5, [6]]]].map {|x| p x; [x, x] }.flatten
1
2
3
[4, [5, [6]]]
=> [1, 1, 2, 2, 3, 3, 4, 5, 6, 4, 5, 6]

あれ? 同じにならない。
実はflattenは引数を取るメソッドで、何段階flatにするのか指定できます。
指定しないと完全にflatにするみたいです。

[3] pry(main)> [1, 2, 3, [4, [5, [6]]]].map {|x| p x; [x, x] }.flatten(1)
1
2
3
[4, [5, [6]]]
=> [1, 1, 2, 2, 3, 3, [4, [5, [6]]], [4, [5, [6]]]]

1階層だけflatにしたら同じ結果になりました。

Scala

scala> List(1, 2, 3, List(4, List(5, List(6)))).flatMap { (x) => println(x); List(x, x) }
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
List(4, List(5, List(6)))      # 4つ目の要素
res2: List[Any] = List(1, 1, 2, 2, 3, 3, List(4, List(5, List(6))), List(4, List(5, List(6))))     # 新しいリスト

mapしてflattenすると、

scala> List(1, 2, 3, List(4, List(5, List(6)))).map{ (x) => println(x); List(x, x) }.flatten
1
2
3
List(4, List(5, List(6)))
res1: List[Any] = List(1, 1, 2, 2, 3, 3, List(4, List(5, List(6))), List(4, List(5, List(6))))

同じになりました。

Swift

println(([1, 2, 3, [4, [5, [6]]]] as [AnyObject]).flatMap { i -> [AnyObject] in println(i); return [i, i]; })
1                              # 1つ目の要素
2                              # 2つ目の要素
3                              # 3つ目の要素
(                              # ---
    4,                         #  |
        (                      #  |
        5,                     #  |
                (              #  | 4つ目の要素
            6                  #  |
        )                      #  |
    )                          #  |
)                              # ---
[1, 1, 2, 2, 3, 3, (           # ---
    4,                         #  |
        (                      #  |
        5,                     #  |
                (              #  |
            6                  #  |
        )                      #  |
    )                          #  |
), (                           #  | 新しいリスト
    4,                         #  |   
        (                      #  |
        5,                     #  |
                (              #  |
            6                  #  |
        )                      #  |
    )                          #  |
)]                             # ---

mapしてflattenしようとしたら、flattenはありませんでした。
しょうがないのでflattenの代わりにflatMapを使って、mapで作った箱をそのまま返す感じで。

println(([1, 2, 3, [4, [5, [6]]]] as [AnyObject]).map { i -> [AnyObject] in println(i); return [i, i]; }.flatMap { $0 })
1
2
3
(
    4,
        (
        5,
                (
            6
        )
    )
)
[1, 1, 2, 2, 3, 3, (
    4,
        (
        5,
                (
            6
        )
    )
), (
    4,
        (
        5,
                (
            6
        )
    )
)]

同じ結果になりました。

JavaScript

> [1, 2, 3, [4, [5, [6]]]].flatMap(function(x){ console.log(x); return [x, x]; })
TypeError: Object 1,2,3,4,5,6 has no method 'flatMap'
    at repl:1:27
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.emit (events.js:95:17)
    at Interface._onLine (readline.js:203:10)
    at Interface._line (readline.js:532:8)
    at Interface._ttyWrite (readline.js:761:14)
    at ReadStream.onkeypress (readline.js:100:10)
    at ReadStream.emit (events.js:98:17)
    at emitKey (readline.js:1096:12)
> [1, 2, 3, [4, [5, [6]]]].flatten
undefined    

そもそも無かった。

まとめ

  • 普通の使い方ならどの言語も全部同じ挙動。当たり前か、、、
  • JavaScriptにはない。
31
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
24