Play Framework 2.4.1: Как удалить элемент из JsArray

Учитывая следующее JSON...

scala>  val js = Json.parse("""{"key1": "value1", "key2": "value2","list":[{"item1": "value1"},{"item2": "value2"}]}""")
js: play.api.libs.json.JsValue = {"key1":"value1","key2":"value2","list":[{"item1":"value1"},{"item2":"value2"}]}

... я получаю первый элемент list как это:

scala> val l = (js \ "list").as[List[JsValue]]
l: List[play.api.libs.json.JsValue] = List({"item1":"value1"}, {"item2":"value2"})

scala> val first = l(0)
first: play.api.libs.json.JsValue = {"item1":"value1"}

... но как мне удалить элемент из list по данному индексу?

2 ответа

Решение

Там нет ничего в JsValue ни JsPath для этого вы можете использовать библиотеку объективов, такую ​​как Monocle. В противном случае, вот один из способов сделать это:

(js \ "list").get match {
  case JsArray(items) => dropAt(items, 1)
}

где dropAt является:

def dropAt[A](items: Seq[A], id: Int): Seq[A] =
  items.zipWithIndex.filter(_._2 != id).map(_._1)

(dropAt это не красиво, но я не знаю ни одного хорошего API для этого.)

Здесь нет dropAt в стандартной коллекции библиотек. Вы можете добавить один, используя шаблон enrich-my-library. При обогащении коллекций вам часто лучше всего использовать CanBuildFrom структура, которая позволит вам сохранить сильные типы. Вы могли бы реализовать dropAt как:

implicit class TraversableDropAt[A, Repr <: Traversable[A]](val xs: TraversableLike[A, Repr]) extends AnyVal {
  def dropAt[That](n: Int)(implicit cbf: CanBuildFrom[Repr, A, That]): That = {
    val bf = cbf()
    bf.sizeHint(xs.size - 1) 
    bf ++= xs.take(n)
    bf ++= xs.drop(n + 1)
    bf.result
  }
}

Это позволит вам позвонить myCollection.dropAt(n) для любого Traversable расширение (например, List, Seq, Iterable, так далее.).

Когда вы работаете с типами PlayJSON, вам часто лучше конвертировать их в обычные типы Scala как можно скорее. Здесь вы можете превратить массив в Seq[JsValue] несколькими способами:

val items = (js \ "list").as[Seq[JsValue]]
val items = (js \ "list").as[JsArray].value
val items = (js \ "list") match { case JsArray(items) => items }
val JsArray(items) = (js \ "list")

Когда у вас есть items Коллекция, вы можете использовать новый dropAt метод на это.

Другим вариантом было бы добавить dropAt метод непосредственно JsArray (или даже JsValue) используя шаблон enrich-my-library, показанный выше.

Если вам нужно преобразовать обратно в JsArray, вы можете использовать Json.arr метод.

Вы можете удалить элементы, преобразовав их в изменяемую коллекцию, а не в неизменяемый список. На изменяемой коллекции вы можете позвонить remove(int: Index) операция, которая изменит коллекцию. Таким образом, ваш случай может быть записан как

val js = Json.parse("""{"key1": "value1", "key2": "value2","list":[{"item1": "value1"},{"item2": "value2"}]}""")
val l = (js \ "list").as[ArrayBuffer[JsValue]]

l.remove(0) // returns the removed element at index 0 but modifies the underlying collection l


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