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 по заданному индексу?


person j3d    schedule 29.06.2015    source источник


Ответы (3)


В JsValue и JsPath для этого нет ничего, вы можете использовать библиотеку Lens, такую ​​как 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 для этого.)

person bjfletcher    schedule 29.06.2015

В стандартной библиотеке коллекций нет dropAt. Вы можете добавить его, используя шаблон обогащения моей библиотеки. При обогащении коллекций часто лучше всего использовать структуру 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) с использованием показанного выше шаблона обогащения моей библиотеки.

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

person Ben Reich    schedule 29.06.2015

Вы можете удалить элементы, преобразовав их в изменяемую коллекцию, а не в неизменяемый список. В изменяемой коллекции вы можете вызвать операцию 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


person sparker    schedule 24.04.2019