Создать новый массив из разреженного массива объектов в JQ - PullRequest
1 голос
/ 16 марта 2019

У меня есть файл JSON, который я хочу обработать с помощью JQ.У него есть массив объектов внутри другого объекта, с ключом, который я хочу использовать для заполнения нового массива.

В моем реальном сценарии использования он вложен во множество других пухов, и там гораздо большемассивы, но возьмем это как более простой, но представительный пример такого рода вещей:

{
  "numbers": [
    {
      "numeral": 1,
      "ordinal": "1st",
      "word": "One"
    },
    {
      "numeral": 2,
      "ordinal": "2nd",
      "word": "Two"
    },
    {
      "numeral": 5,
      "ordinal": "5th",
      "word": "Five"
    },
    {
      "some-other-fluff-i-want-to-ignore": true
    }
  ]
}

Я хотел бы использовать JQ, чтобы получить новый массив на основе элементов, игнорируя некоторые элементы и обрабатывая отсутствующие,например,

[
  "The 1st word is One",
  "The 2nd word is Two",
  "Wot no number 3?",
  "Wot no number 4?",
  "The 5th word is Five"
]

Делать это в цикле для элементов, которые там есть, просто, кратко и достаточно элегантно:

.numbers | map( . | select( .numeral) | [ "The", .ordinal, "word is", .word ] | join (" "))

Но я не могу найти способ справиться спропущенные записи.У меня есть некоторый код, который вроде работает:

.numbers | [
  ( .[] | select(.numeral == 1) | ( [ "The", .ordinal, "word is", .word ] | join (" ")) ) // "Wot no number 1?",
  ( .[] | select(.numeral == 2) | ( [ "The", .ordinal, "word is", .word ] | join (" ")) ) // "Wot no number 2?",
  ( .[] | select(.numeral == 3) | ( [ "The", .ordinal, "word is", .word ] | join (" ")) ) // "Wot no number 3?",
  ( .[] | select(.numeral == 4) | ( [ "The", .ordinal, "word is", .word ] | join (" ")) ) // "Wot no number 4?",
  ( .[] | select(.numeral == 5) | ( [ "The", .ordinal, "word is", .word ] | join (" ")) ) // "Wot no number 5?"
]

Он производит полезный вывод, по моде:

richard@sophia:~$ jq -f make-array.jq < numbers.json
[
  "The 1st word is One",
  "The 2nd word is Two",
  "Wot no number 3?",
  "Wot no number 4?",
  "The 5th word is Five"
]
richard@sophia:~$ 

Однако, пока он производит вывод, обрабатывает недостающие элементы иигнорирует биты, которые мне не нужны, это, очевидно, чрезвычайно наивный код, который требует цикла for или чего-то подобного, но я не вижу способа сделать это в JQ.Есть идеи?

Ответы [ 2 ]

2 голосов
/ 16 марта 2019

jq решение:

jq 'def print(o): "The \(o.ordinal) word is \(o.word)";
    .numbers | (reduce map(select(.numeral))[] as $o ({}; .["\($o.numeral)"] = $o)) as $o
    | [range(0; ($o | [keys[] | tonumber] | max)) 
       | "\(.+1)" as $i
       | if ($o[$i]) then print($o[$i]) else "Wot no number \($i)?" end
    ]' input.json

Выход:

[
  "The 1st word is One",
  "The 2nd word is Two",
  "Wot no number 3?",
  "Wot no number 4?",
  "The 5th word is Five"
]
1 голос
/ 16 марта 2019

Другое решение!

jq  '[ 
       range(1; ( .numbers | max_by(.numeral)|.numeral ) +1 ) as $range_do_diplay  | 
       .numbers as $thedata | $range_do_diplay |
       . as $i | 
            if ([$thedata[]|contains( { numeral: $i })]|any ) 
            then 
               ($thedata|map(select( .numeral == $i )))|.[0]| "The \(.ordinal) word is \(.word) " 
            else 
               "Wot no number \($i)?" 
            end  
     ] ' numbers.json

В этом решении используется

  • max_by , чтобы найти максимальное значение цифра
  • диапазон для генерации списка значений o
  • использовать переменные для хранения промежуточного значения
...