Преобразование списка в карту с помощью 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)).

Другие вопросы по тегам