Scala:如何将元组元素转换为列表

14
假设我有以下元组列表:
val tuples = listOfStrings.map(string => {
            val split = string.split(":")
            (split(0), split(1), split(2))
        })

我想把split(0)放进一个列表,把split(1)放进另一个列表,以此类推。 实现这个功能的简单方法是:

list1 = tuples.map(x => x._1).toList
list2 = tuples.map(x => x._2).toList
list3 = tuples.map(x => x._3).toList

有没有更加优雅(函数式)的方法,可以在不写三个单独语句的情况下达到上述效果?

5个回答

13
这将会给你一个列表嵌套列表的结果:
tuples.map{t => List(t._1, t._2, t._3)}.transpose

如果您想将它们存储在本地变量中,只需执行以下操作:
val List(l1,l2,l3) = tuples.map{t => List(t._1, t._2, t._3)}.transpose

更新: 正如Blaisorblade所指出的那样,标准库实际上有一个内置方法来处理这个问题:unzip3,它类似于unzip,但是用于三元组而不是对。

val (l1, l2, l3) = tuples.unzip3

毋庸置疑,与我上述手动解决方案相比,您应该更青睐这种方法(但对于元组的度数> 3,仍然适用)。

准备发布:tuples.map{t => t._1 :: t._2 :: t._3 :: Nil}.transpose 很不错。+1 - gilad hoch
不好的想法,您应该使用来自其他答案的解压缩:https://dev59.com/pGct5IYBdhLWcg3wCZMT#17281359 - Blaisorblade
@Balisorblade:我不得不反驳一下。如果这是一个Tuple2,那么这个说法是正确的,但它是一个Tuple3unzip只处理解压成对的操作,并且jeshan自己的示例展示了一个Tuple3列表,而不是Tuple2 - Régis Jean-Gilles
@RégisJean-Gilles:所以你需要unzip3。很遗憾(或者是一个bug),只支持对偶和三元组 - shapeless支持HList,但是(http://docs.typelevel.org/api/shapeless/stable/1.2.4/doc_2.10/index.html#shapeless.HListOps)。 - Blaisorblade
此外,您可以使用 tuple.productIterator.toList 将元组转换为 (of Any) 列表。因此:def unzip(l: List[Product]) = (l map (_.productIterator.toList)).transpose - Blaisorblade
是的,但是(如已经提到的),使用'productIterator'时,您必须添加一个转换。对于unzip3来说是个好发现,我不知道它存在于标准库中。 - Régis Jean-Gilles

11
你想要解压缩:
scala> val (numbers, homonyms) = List(("one", "won"), ("two", "too")).unzip
numbers: List[java.lang.String] = List(one, two)
homonyms: List[java.lang.String] = List(won, too)

2
这仅适用于对,而不适用于任何其他类型的元组。 - Régis Jean-Gilles
1
有 unzip3 用于三元组。 - Blaisorblade

3

如果您需要处理任意元组大小的东西:

val tupleSize = 3
0.until(tupleSize).toList
    .map(x => (_:Product).productElement(x).asInstanceOf[String])
    .map(tuples.map(_))

显然,如果您有一个数组列表,那么这个问题可以更加优雅地表达。

1
或者简单地说:tuples.map(_.productIterator.toList).transpose.asInstanceOf[List[List[String]]。我本来想发布这个解决方案,但是强制转换有点可惜。 - Régis Jean-Gilles
我也考虑过那个解决办法,但这个版本可能比转置更高效。如果我们将一个ListArray作为输入,就可以避免强制类型转换。 - Kim Stebel
4
使用“_1”,“_2”,“_3”等命名方案的解决方案具有正确类型而无需运行时转换的优势。在上述使用productIterator或productElement时,返回的结果被类型定义为“Any”,因此需要进行转换。 - Régis Jean-Gilles
1
当然,真正的解决方案是使用 shapeless - Kim Stebel
1
真正的解决方案是不使用元组。 - Luigi Plinge

1

你可以将语句写在一行中。

就像这样

 (list1, list2, list3) = tuples.foldRight((List[String](), List[String](), List[String]()))( (a,b) => (a._1 :: b._1, a._2 :: b._2, a._3 :: b._3 ) )

0

我不知道是否优雅,但是你可以在不需要存储元组的中间步骤的情况下在一行代码中完成它。也许这有点难以阅读...

(for(split <- listOfStrings.map(_.split(":")))
  yield List(split(0), split(1), split(2))).transpose

repl 示例:

scala> listOfStrings
res1: List[java.lang.String] = List(a:b:c, d:e:f, g:h:i)

scala> (for(split <- listOfStrings.map(_.split(":")))
     |   yield List(split(0), split(1), split(2))).transpose
res2: List[List[java.lang.String]] = List(List(a, d, g), List(b, e, h), List(c, f, i))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接