Могу ли я объединить в Scala более двух списков?


94

Учитывая следующий список Scala:

val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))

Как я могу получить:

List(("a1", "a2", "a3"), ("b1", "b2", "b3"), ("c1", "c2", "c3"))

Поскольку zip можно использовать только для объединения двух списков, я думаю, вам нужно как-то перебрать / уменьшить основной список. Неудивительно, что следующее не работает:

scala> l reduceLeft ((a, b) => a zip b)
<console>:6: error: type mismatch;
 found   : List[(String, String)]
 required: List[String]
       l reduceLeft ((a, b) => a zip b)

Есть предложения, как это сделать? Думаю, мне не хватает очень простого способа сделать это.

Обновление: я ищу решение, которое может взять список из N списков с M элементами каждый и создать список из M TupleN.

Обновление 2: как оказалось, для моего конкретного варианта использования лучше иметь список списков, а не список кортежей, поэтому я принимаю ответ тыквы. Это также самый простой способ, поскольку в нем используется собственный метод.



Определенно стоит отметить: stackoverflow.com/questions/1683312/…
Sudheer Aedama

@VenkatSudheerReddyAedama Также я спросил, пять дней спустя. ;-)
pr1001

Ответы:


36

Я не верю, что можно сгенерировать список кортежей произвольного размера, но функция транспонирования делает именно то, что вам нужно, если вы не против получить вместо этого список списков.


Спасибо, отлично работает! Когда я перехожу к своему конкретному варианту использования, я вижу, что список списков в любом случае был бы лучше, поскольку мне нужно сопоставить и сократить различные подсписки.
pr1001 03

2
@JoshCason в самом узком смысле слова "больше двух", конечно. Три действительно больше, чем два. Я истолковал вопрос в более широком смысле «более двух», что означает произвольно много. И в этом случае невозможно сделать то, что требует вопрос, если вы не дойдете до HLists и т.п.
copumpkin

ссылка в ответе не работает, новая ссылка scala-lang.org/api/2.12.1/scala/…
Рамеш Махарджан

216
scala> (List(1,2,3),List(4,5,6),List(7,8,9)).zipped.toList
res0: List[(Int, Int, Int)] = List((1,4,7), (2,5,8), (3,6,9))

Для дальнейшего использования.


32
Это отлично подходит для архивирования трех списков. Жаль, что это не работает для более чем трех списков :(
theon

2
Обратите внимание, что это должно быть сначала в кортеже: zippedэто не функция List.
Натаниэль Форд

6
zippedустарела в Scala 2.13. в 2.13, dol1.lazyZip(l2).lazyZip(l3).toList
Сет Тисью

30

Таким образом, этот фрагмент кода не отвечает потребностям OP, и не только потому, что это поток четырехлетней давности, но он действительно отвечает на вопрос заголовка, и, возможно, кто-то даже может найти его полезным.

Чтобы заархивировать 3 коллекции:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}

сделать 4 коллекции выглядит так:as zip bs zip cs zip ds map { case ((a,b),c)} map {case ((a,b),c,d)=>(a,b,c,d)}
Джеймс Тобин

1
@JamesTobin, вы сокращаете доas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
keepcoding

Подходит для списков разного типа.
FP Freely

11

Да, с zip3 .


2
Спасибо, но работает только с 3 списками. Я ищу решение, которое может взять список из N списков с M элементами в каждом и создать список из M кортежей.
pr1001 03

6

transposeделает свое дело. Возможный алгоритм:

def combineLists[A](ss:List[A]*) = {
    val sa = ss.reverse;
    (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1))
}

Например:

combineLists(List(1, 2, 3), List(10,20), List(100, 200, 300))
// => List[List[Int]] = List(List(1, 10, 100), List(2, 20, 200))

Ответ усекается до размера самого короткого списка во входных данных.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))

1
этот ответ почти помогает, однако он меняет местами элементы. Можете ли вы предложить улучшенную версию, которая выводит результат в ожидаемом порядке? спасибо
fracca

Модифицированная версия, сохраняющая порядок: def combineLists[A](ss:List[A]*) = { val sa = ss.reverse; (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1)) }
rogermenezes

5

Scala рассматривает все разных размеров его кортежа разных классов ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22) в то время как они все наследуются от Productпризнака, что черта не несет достаточно информации , чтобы реально использовать значение данных из различных размеров кортежей если бы все они могли быть возвращены одной и той же функцией. (И дженерики scala также недостаточно мощны, чтобы справиться с этим случаем.)

Лучше всего написать перегрузки функции zip для всех 22 размеров кортежей. Генератор кода, вероятно, поможет вам в этом.


5

Если вы не хотите идти по пути аппликативного scalaz / cats / (вставьте сюда свою любимую функциональную библиотеку), то лучше всего подойдет сопоставление с образцом, хотя (_, _)синтаксис немного неудобен для вложенности, поэтому давайте изменим его:

import scala.{Tuple2 => &}

for (i1 & i2 & i3 & i4 <- list1 zip list2 zip list3 zip list4) yield (i1, i2, i3, i4)

&Произвольный выбор здесь, все , что выглядит красиво инфикс должен сделать это. Однако во время проверки кода вы, скорее всего, удивитесь.

Он также должен работать со всем, что вы можете zip(например, Futureс)


5

Я не верю, что это возможно без повторения. По одной простой причине: вы не можете определить тип возврата запрашиваемой функции.

Например, если ваш ввод был List(List(1,2), List(3,4)), то возвращаемый тип будет List[Tuple2[Int]]. Если бы у него было три элемента, тип возвращаемого значения был бы List[Tuple3[Int]]и так далее.

Вы можете вернуться List[AnyRef]или даже List[Product]сделать несколько дел, по одному для каждого условия.

Что касается общей транспозиции списка, это работает:

def transpose[T](l: List[List[T]]): List[List[T]] = l match {
  case Nil => Nil
  case Nil :: _ => Nil
  case _ => (l map (_.head)) :: transpose(l map (_.tail))
}

Это не сработает для списков произвольного размера. Например: транспонировать (List (List («a», «b»), List («c»)))
Sudheer Aedama

1
@VenkatSudheerReddyAedama Транспонирование неполных матриц для меня не имеет смысла. Чтобы взять ваш пример, если cв соответствии с aили с b? И как бы вы изобразили, что это соответствует другому?
Дэниел С. Собрал,

Согласовано. Это неполная матрица. Я искал что-то вроде zipAll. Скажем, в моем случае cсоответствует a(т.е. соответствует индексу)?
Судхир Аэдама,

2

product-collections имеет flatZipоперацию до 22 arity.

scala> List(1,2,3) flatZip Seq("a","b","c") flatZip Vector(1.0,2.0,3.0) flatZip Seq(9,8,7)
res1: com.github.marklister.collections.immutable.CollSeq4[Int,String,Double,Int] = 
CollSeq((1,a,1.0,9),
        (2,b,2.0,8),
        (3,c,3.0,7))

0

Со Скалазом:

import scalaz.Zip
import scalaz.std.list._

// Zip 3
Zip[List].ap.tuple3(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"))

// Zip 4
Zip[List].ap.tuple4(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"))

// Zip 5
Zip[List].ap.tuple5(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"))

Более 5:

// Zip 6
Zip[List].ap.apply6(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"))((_, _, _, _, _, _))

// Zip 7
Zip[List].ap.apply7(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"),
                    List("a7", "b7"))((_, _, _, _, _, _, _))

...

// Zip 12
Zip[List].ap.apply12(List("a1", "b1"),
                     List("a2", "b2"),
                     List("a3", "b3"),
                     List("a4", "b4"),
                     List("a5", "b5"),
                     List("a6", "b6"),
                     List("a7", "b7"),
                     List("a8", "b8"),
                     List("a9", "b9"),
                     List("a10", "b10"),
                     List("a11", "b11"),
                     List("a12", "b12"))((_, _, _, _, _, _, _, _, _, _, _, _))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.