Scala: преобразование списка во вложенную карту - PullRequest
0 голосов
/ 19 июня 2020

У меня есть класс ConfigEntry, определенный как

case class ConfigEntry(
  key:    String,
  value:  String
)

, и список:


val list: List[ConfigEntry] = List(
  ConfigEntry("general.first", "general first value"),
  ConfigEntry("general.second", "general second value"),
  ConfigEntry("custom.first", "custom first value"),
  ConfigEntry("custom.second", "custom second value")
)

Учитывая список ConfigEntry, мне нужна карта из свойства -> карта записей, которые удовлетворить это свойство

Например, если у меня есть

def getConfig: Map[String, Map[String, String]] = {

  def getKey(key: String, index: Int): String = key.split("\\.")(index)

  list.map { config =>
    getKey(config.key, 0) -> Map(getKey(config.key, 1) -> config.value)
  }.toMap

}

, я получаю результат

res0: Map[String,Map[String,String]] =
  Map(
    "general" ->
      Map("second" -> "general second value"),
    "custom" ->
      Map("second" -> "custom second value")
  )

, и он должен быть

res0: Map[String,Map[String,String]] =
Map(
  "general" ->
    Map(
      "first" -> "general first value",
      "second" -> "general second value"
    ),
  "custom" ->
    Map(
      "first" -> "custom first value",
      "second" -> "custom second value"
    )
)

Первая запись из списка отсутствует. Вероятно, через .toMap

Как я могу это сделать?

Спасибо за любую предоставленную помощь

Ответы [ 3 ]

2 голосов
/ 19 июня 2020

Ваша карта даст только результат 1: 1. Для того, чтобы делать то, что вы хотите, вам понадобится аккумулятор (существующая карта). вместо этого можно применить foldLeft к вашему списку с пустой картой в качестве начального значения.

list.foldLeft(Map.empty[String, Map[String, String]]) { (configs, configEntry) =>
  val primaryKey = getKey(configEntry.key, 0)
  val secondaryKey = getKey(configEntry.key, 1)

  configs.get(primaryKey) match {
    case None =>
      configs.updated(primaryKey, Map(secondaryKey -> configEntry.value))
    case Some(configMap) =>
      configs.updated(primaryKey, configMap.updated(secondaryKey, configEntry.value))
  }
}
1 голос
/ 19 июня 2020

Вы можете сделать что-то вроде этого:

final case class ConfigEntry(
    key:    String,
    value:  String
)

type Config =  Map[String, Map[String, String]]

def getConfig(data: List[ConfigEntry]): Config =
  data
    .view
    .map(e => e.key.split('.').toList -> e.value)
    .collect {
      case (k1 :: k2 :: Nil, v) => k1 -> (k2 -> v)
    }.groupMap(_._1)(_._2)
    .view
    .mapValues(_.toMap)
    .toMap

Или что-то вроде этого:

def getConfig(data: List[ConfigEntry]): Config = {
  @annotation.tailrec
  def loop(remaining: List[ConfigEntry], acc: Config): Config =
    remaining match {
      case ConfigEntry(key, value) :: xs =>
        val newAcc = key.split('.').toList match {
          case k1 :: k2 :: Nil =>
            acc.updatedWith(k1) {
              case Some(map) =>
                val newMap = map.updatedWith(k2) {
                  case Some(v) =>
                    println(s"Overwriting previous value ${v} for the key: ${key}")
                    // Just overwrite the previous value.
                    Some(value)

                  case None =>
                    Some(value)
                }
                Some(newMap)

              case None =>
                Some(Map(k2 -> value))
            }

          case _ =>
            println(s"Bad key: ${key}")
            // Just skip this key.
            acc
        }
        loop(remaining = xs, newAcc)

      case Nil =>
        acc
    }

  loop(remaining = data, acc = Map.empty)
}

Я оставляю обработку ошибок, таких как дублированные или плохие ключи, считывателю.


Кстати, поскольку это конфигурация, рассматривали ли вы возможность использования библиотеки Config?

0 голосов
/ 19 июня 2020

Просто:

list.map { ce =>
  val Array(l, r) = ce.key.split("\\.")
  l -> (r -> ce.value)
}                                                 // List[(String, (String, String))]
.groupBy { case (k, _) => k }                     // Map[String, List[(String, (String, String))]]
.view.mapValues(_.map { case (_, v) => v }.toMap) // MapView[String, List[(String, String)]]
.toMap                                            // Map[String, Map[String, String]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...