Могу ли я объединить в 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: как оказалось, для моего конкретного варианта использования лучше иметь список списков, а не список кортежей, поэтому я принимаю ответ тыквы. Это также самый простой способ, поскольку в нем используется собственный метод.

pr1001
источник
возможный дубликат нескольких последовательностей Zip
Suma
Определенно стоит отметить: 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с)

L4Z
источник
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"))((_, _, _, _, _, _, _, _, _, _, _, _))
ЖекаКозлов
источник