Home > front end >  Merge multiple objects under a single object and add sibling object in one command
Merge multiple objects under a single object and add sibling object in one command

Time:09-17

So I have three files:

cats.json

{
  "cats": [
    {
      "name": "fluffles",
      "age": 10,
      "color": "white"
    }
  ]
}

dogs.json

{
  "dogs": [
    {
      "name": "sam",
      "age": 5,
      "color": "black and white"
    },
    {
      "name": "rover",
      "age": 2,
      "color": "brown and white"
    }
  ]
}

snakes.json

{
  "snakes": [
    {
      "name": "noodles",
      "age": 10,
      "color": "green"
    }
  ]
}

I wanted to merge these together, under an "animals" object. I've found that will merge the files:

jq -s '{"animals": .} ' cats.json dogs.json snakes.json > animals.json
{
  "animals": [
    {
      "cats": [
        {
          "name": "fluffles",
          "age": 10,
          "color": "white"
        }
      ]
    },
    {
      "dogs": [
        {
          "name": "sam",
          "age": 5,
          "color": "black and white"
        },
        {
          "name": "rover",
          "age": 2,
          "color": "brown and white"
        }
      ]
    },
    {
      "snakes": [
        {
          "name": "noodles",
          "age": 10,
          "color": "green"
        }
      ]
    }
  ]
}

Now I have an additional object:

owners.json

{
  "owners": [
    "peter",
    "william",
    "sally"
  ]
}

which I want to merge into the same file using

jq -s '.[0]   .[1]' animals.json owners.json

Can I do both of these operations with just one jq command?

jq -s '{"animals": .} ' cats.json dogs.json snakes.json > animals.json
jq -s '.[0]   .[1]' animals.json owners.json

The result would look like this:

{
  "animals": [
    {
      "cats": [
        {
          "name": "fluffles",
          "age": 10,
          "color": "white"
        }
      ]
    },
    {
      "dogs": [
        {
          "name": "sam",
          "age": 5,
          "color": "black and white"
        },
        {
          "name": "rover",
          "age": 2,
          "color": "brown and white"
        }
      ]
    },
    {
      "snakes": [
        {
          "name": "noodles",
          "age": 10,
          "color": "green"
        }
      ]
    }
  ],
  "owners": [
    "peter",
    "william",
    "sally"
  ]
}

CodePudding user response:

Not sure if this is the way-to-go, but it gets the desired output by:

  • Using --slurp:
  • Catching the first 3 files as a single array variable
    [ .[0] * .[1] * .[2] ] as $all
  • Catching owners object as a single variable
    .[3].owners as $owners
  • Creating the object as desired
    { "animals": $all, "owners": $owners }
jq \
    --slurp \
    '[ .[0] * .[1] * .[2] ] as $all | .[3].owners as $owners | { "animals": $all, "owners": $owners }' cats.json dogs.json snakes.json owners.json

Will produce:

{
  "animals": [
    {
      "cats": [
        {
          "name": "fluffles",
          "age": 10,
          "color": "white"
        }
      ],
      "dogs": [
        {
          "name": "sam",
          "age": 5,
          "color": "black and white"
        },
        {
          "name": "rover",
          "age": 2,
          "color": "brown and white"
        }
      ],
      "snakes": [
        {
          "name": "noodles",
          "age": 10,
          "color": "green"
        }
      ]
    }
  ],
  "owners": [
    "peter",
    "william",
    "sally"
  ]
}

CodePudding user response:

Suppose you had an (a priori) indeterminate or large numbers of types of animals, and just one owners file. In such cases, it would be better (to save memory) not to use the -s option, and it would be easier to invoke jq with the owners file as the first data file, e.g. along the lines of:

jq -n -f program.jq owners.json $(ls *.json | grep -v owners.json)

where program.jq contains a program such as:

input as $owners | {$owners, animals: [inputs]}

(Notice how {"owners": $owners} can be abbreviated.)

  • Related