Преобразование списка в карту с помощью foldLeft
Используя приведенный ниже код, я пытаюсь произвести
Map(2017-06-03 09:25:30 -> List( ("c",2190.79) , ("d",24.11), ("d",24.11), ("d",24.11) ),
2017-06-03 09:25:40 -> List( ("b",24.62) , ("b",24.62)) ,
2017-06-03 09:25:50 -> List( ("a",194.55) , ("a",194.55)) )
от
val l = List("a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30",
"a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30")
Вот полный код:
object Main extends App {
val l = List("a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30",
"a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30")
case class Details(date : java.util.Date , det : (String , Float))
val format = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
val p = l.map(m => new Details(format.parse(m.split(",")(2)), ( m.split(",")(0),m.split(",")(1).toFloat) ))
val s = p.sortBy(r => (r.date))
val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) { (m, s) => (m , List(s)) }
}
Линия:
val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) { (m, s) => (m , List(s)) }
вызывает следующую ошибку компиляции:
[ошибка] найдена: (scala.collection.immutable.Map[java.util.Date,List[(String, Float)]], список [Main.Details]) [ошибка] требуется: scala.collection.immutable.Map[java.util.Date,List[(String, Float)]] [error] val map = s.foldLeft(Mapjava.util.Date, List[(String, Float)]) { (m, s) => (m, List(s)) } [ошибка]
^ [ошибка] найдена одна ошибка [ошибка] (компиляция:compileIncremental) Ошибка компиляции [ошибка] Общее время: 2 с, завершено 11 июня 2017 г. 22:51:46
Я не использую map
правильно?
4 ответа
Проблема, с которой вы сталкиваетесь, исходит от анонимной функции, которую вы пытаетесь интегрировать в свою карту нового кортежа; что вы делаете:
{ (m, s) => (m, List(s)) }
куда m
имеет тип Map[Date, List[(String , Float)]]
а также s
имеет тип Details
,
(m, List(s))
синтаксис означает, что вы создаете пару, составленную из карты m
и одноэлементный список, который содержит s
,
Вместо этого вы хотите поместить два элемента в s
как новая пара m
что-то, чего вы можете достичь, выполнив следующее:
{ (m, s) => m.updated(s.date, s.det :: m.get(s.date).getOrElse(List.empty)) }
Давайте посмотрим, что здесь происходит: вы берете карту аккумулятора m
и обновлять его на каждом повороте сгиба s.date
в качестве ключа, а затем значение. Значение - это ранее сохраненное значение для этого ключа (m.get(s.date)
, чтобы убедиться, что мы не перезаписываем этот ключ) или пустой список, если по-прежнему нет значения, с добавлением значения, на которое мы смотрим сейчас, пока сгиб пересекает коллекцию.
Это решает проблему, но, как вы можете видеть, то, что вы делаете, является широко известной операцией группировки, и API Scala Collection уже предоставляет вам базовую инфраструктуру для достижения вашей цели.
Вы можете изменить свой код следующим образом и получить тот же результат:
object Main extends App {
val l = List("a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30",
"a,194.55,2017-06-03 09:25:50",
"b,24.62,2017-06-03 09:25:40",
"c,2190.79,2017-06-03 09:25:30",
"d,24.11,2017-06-03 09:25:30")
val format = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
val map =
l.groupBy(m => format.parse(m.split(",")(2))).
mapValues(l => l.map(m => (m.split(",")(0),m.split(",")(1).toFloat)))
}
Как видите, я использовал groupBy
комбинатор с parse
метод вашего форматера. Эта функция, однако, представляет в качестве значений результирующей группировки весь элемент, в то время как вы хотели только его части (именно поэтому я в дальнейшем использовал mapValues
комбинатор).
Если вас больше интересует порядок, в котором ваша карта выставляет ваши элементы, не забудьте использовать карту, которая обеспечивает какой-то порядок (например, SortedMap
).
Это не исключение, а ошибка компиляции. Ошибка объясняет, что не так с вашим кодом:
Второй аргумент foldLeft
(указано ^
в сообщении об ошибке) должна быть функция (B, A) ⇒ B
, Ваш код имеет (B, A) ⇒ (B, A)
вместо...
Я думаю, что цель может быть достигнута чуть более напрямую.
val format = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
l.map(_.split(","))
.groupBy(a => format.parse(a(2)))
.mapValues(_.map(a => (a(0),a(1).toFloat))) //Map[java.util.Date,List[(String, Float)]]
Вот как исправить эту строку:
val map = s.foldLeft(Map[java.util.Date, List[(String , Float)]]()) {
(m, s) =>
m +
(s.date ->
(s.det :: m.getOrElse(s.date, List[(String , Float)]()))
)
}
Для каждой итерации fold
вам нужно вернуть обновленную карту m
,
Для этого нужно проверить, m
уже содержит s.date
, Если да, добавьте новый s.det
к существующему значению списка и поместите обновленный список обратно в карту.
Если это первое вхождение s.date
просто создай пустой список, поставь s.det
в него, а затем положить список обратно m
,
Обратите внимание, что значения результирующей карты могут быть в обратном порядке (так как я использую cons (::
), который более эффективен, чем добавление для List
, Вы можете полностью изменить полученные значения, используя map.mapValues(_.reverse)
).