Не знаете, как оптимально выполнить преобразование списка в Scala
Кто-нибудь знает хороший способ превратить следующий список входных данных в список нужных результатов ниже?
Функция, которую я пытаюсь создать
def transformList(input:List[(String,String)]):List[(String,String)] = ???
вход
val inputList = List(
("class","testClass1"),
("class","testClass2"),
("id","testId1"),
("class","testClassRepeat"),
("class","testClassRepeat"),
("id","testId2"),
("href","testHref1")
)
желаемый выход
List(
("class","testClass1 testClass2 testClassRepeat testClassRepeat"),
("id","testId1 testId2"),
("href","testHref1")
)
У меня есть решение, но я не думаю, что делаю это хорошим/эффективным способом. В настоящее время я использую следующее решение:
- Создать пустую измененную карту
- Прокрутите список ввода с помощью .foreach
- Нажатие клавиши/значений на основе inputList в изменчивой карте. Затем добавление к значениям существующих ключей, если это применимо (например, в моем примере ввода списка есть 4 "класса".)
Спасибо,
Фил
Ответы
Ответ 1
Вы можете использовать groupBy и выполняться в одной строке.
scala> inputList.groupBy(_._1).
map{ case (key, value) => (key, value.map(_._2).mkString(" "))}.toList
res0: List[(String, String)] = List(
(href,testHref1),
(class,testClass1 testClass2 testClassRepeat testClassRepeat),
(id,testId1 testId2)
)
Ответ 2
def f(xs: List[(String, String)]): Map[String, List[String]] =
xs.foldRight(Map.empty[String, List[String]]){
(elem: (String, String), acc: Map[String, List[String]]) =>
val (key, value) = elem
acc.get(key) match {
case None => acc + (key -> List(value))
case Some(ys) => acc.updated(key, value :: ys)
}
}
scala> f(inputList)
res2: Map[String,List[String]] = Map(
href -> List(testHref1),
id -> List(testId1, testId2),
class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat)
)
Ответ 3
Может быть, groupBy()
- это то, что вы ищете?
scala> inputList.groupBy(_._1)
res0: Map[String,List[(String, String)]] = Map(
href -> List((href,testHref1)),
class -> List((class,testClass1), (class,testClass2), (class,testClassRepeat), (class,testClassRepeat)),
id -> List((id,testId1), (id,testId2))
)
Также довольно просто очистить список кортежей, пока мы на нем, например.
scala> inputList.groupBy(_._1).map(kv => (kv._1, kv._2.map(_._2)))
res1: Map[String,List[String]] = Map(
href -> List(testHref1),
class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat),
id -> List(testId1, testId2)
)
Ответ 4
Вы можете использовать foldLeft для сортированной коллекции:
def transformList(input:List[(String,String)]):List[(String,String)] =
input
.sortBy(_._1)
.foldLeft(List[(String, String)]()) {
case ((xn,xv)::xs, (name, value)) if xn==name => (xn, xv + " " + value)::xs
case (xs, item) => item::xs
}
Ответ 5
Еще один способ с пониманием, но также используя groupBy
(см. @hezamu):
val m =
for { (k, xs) <- inputList.groupBy(_._1)
s = xs.map(_._2).mkString(" ")
}
yield k -> s
а затем
m.toMap
Ответ 6
Вы можете использовать groupBy
с for comprehension
, чтобы сделать код более встроенным с реляционными концепциями SQL, в котором нужный результат аналогичен ключу Group By
on, а затем преобразует сгруппированный результат, в этом случае конкатенацию строка:
def transformList(input:List[(String,String)]):List[(String,String)] = {
(for {
// Return the generator for key-values of grouped result
(k, v) <- input.groupBy(y => y._1)
// For every list in the grouped result return the concatenated string
z = v.map(_._2).mkString(" ")
} yield k -> z)
.toList
}