7

Let's say we have 2 json arrays. How to merge them into a single array with circe? Example:

Array 1:

[{"id": 1}, {"id": 2}, {"id": 3}]

Array 2:

[{"id": 4}, {"id": 5}, {"id": 6}]

Needed:

[{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 6}]

I've tried deepMerge, but it only keeps the contents of the argument, not of the calling object.

Haspemulator
  • 11,050
  • 9
  • 49
  • 76

1 Answers1

11

Suppose we've got the following set-up (I'm using circe-literal for convenience, but your Json values could come from anywhere):

import io.circe.Json, io.circe.literal._

val a1: Json = json"""[{"id": 1}, {"id": 2}, {"id": 3}]"""
val a2: Json = json"""[{"id": 4}, {"id": 5}, {"id": 6}]"""

Now we can combine them like this:

for { a1s <- a1.asArray; a2s <- a2.asArray } yield Json.fromValues(a1s ++ a2s)

Or:

import cats.std.option._, cats.syntax.cartesian._

(a1.asArray |@| a2.asArray).map(_ ++ _).map(Json.fromValues)

Both of these approaches are going to give you an Option[Json] that will be None if either a1 or a2 don't represent JSON arrays. It's up to you to decide what you want to happen in that situation .getOrElse(a2) or .getOrElse(a1.deepMerge(a2)) might be reasonable choices, for example.

As a side note, the current contract of deepMerge says the following:

Null, Array, Boolean, String and Number are treated as values, and values from the argument JSON completely replace values from this JSON.

This isn't set in stone, though, and it might not be unreasonable to have deepMerge concatenate JSON arrays—if you want to open an issue we can do some more thinking about it.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680