Как заставить jq возвращать уникальные результаты, когда в json несколько идентичных записей? - PullRequest
1 голос
/ 17 июня 2019
jq '
  .[]|select(.accountEnabled==true)|select(.assignedPlans[].service=="exchange" and .assignedPlans[].capabilityStatus=="Enabled").proxyAddresses[]'

Ниже приведен пример json, анонимный вывод «az ad user list» (получение списка пользователей Active Directory из Azure) с удалением ненужных вещей. Выше приведена команда jq, которую я хочу использовать для извлечения адресов электронной почты, желаемый результат - «SMTP: russell.coker@example.com», напечатанный один раз, а не 9 раз. Да, я знаю, что могу передать это Unix-команде "sort -u", но я бы хотел выполнить другие json-запросы.

[
  {
    "accountEnabled": true,
    "assignedPlans": [
      {
        "capabilityStatus": "Enabled",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Enabled",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Enabled",
        "service": "exchange"
      }
    ],
    "provisionedPlans": [
      {
        "capabilityStatus": "Enabled",
        "provisioningStatus": "Success",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Enabled",
        "provisioningStatus": "Success",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Enabled",
        "provisioningStatus": "Success",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Enabled",
        "provisioningStatus": "Success",
        "service": "exchange"
      }
    ],
    "proxyAddresses": [
      "SMTP:russell.coker@example.com"
    ]
  },
  {
    "accountEnabled": true,
    "assignedPlans": [
      {
        "capabilityStatus": "Deleted",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Deleted",
        "service": "OfficeForms"
      }
    ],
    "provisionedPlans": [
      {
        "capabilityStatus": "Deleted",
        "provisioningStatus": "Success",
        "service": "SharePoint"
      },
      {
        "capabilityStatus": "Deleted",
        "provisioningStatus": "Success",
        "service": "exchange"
      },
      {
        "capabilityStatus": "Deleted",
        "provisioningStatus": "Success",
        "service": "exchange"
      }
    ],
    "proxyAddresses": [
      "smtp:a@example.com",
      "smtp:b@example.com",
      "SMTP:c@example.com"
    ]
  }
]

Ответы [ 2 ]

1 голос
/ 17 июня 2019

Возможно, проблема в том, что данный jq-запрос просто «неправильный» в том смысле, что он не фиксирует намерение ОП.

Даже если следующий запрос не отражает намерения ОП, стоит отметить, чтос данным JSON он выдает единственный требуемый результат:

.[]
| select(.accountEnabled==true)
| select(any(.assignedPlans[];
             .service=="exchange" and
             .capabilityStatus=="Enabled"))
| .proxyAddresses[]

Аналогично ....

Вот еще один запрос с другой семантикой, но который с данным JSON такжедает единственный желаемый результат.(Это говорит о том, что отдельный пример сам по себе не заменит требования.)

.[]
 | select(.accountEnabled==true)
 | select(any(.assignedPlans[]; .service=="exchange"))
 | select(any(.assignedPlans[]; .capabilityStatus=="Enabled"))
 | .proxyAddresses[]
1 голос
/ 17 июня 2019

Выше приведена команда jq, которую я хочу использовать

Следующий ответ сфокусирован на вышеуказанном требовании.

unique/0 можно использовать, если вы не возражаете против того факта, что он сортирует свои данные. Этот фильтр ожидает массив в качестве входных данных, и вы можете изменить свой запрос следующим образом:

[.[]
 | select(.accountEnabled==true)
 | select(.assignedPlans[].service=="exchange" and .assignedPlans[].capabilityStatus=="Enabled")
 | .proxyAddresses[]]
| unique

Это создает массив, поэтому, если вам нужен поток, просто добавьте [] в конце.

Потоково-ориентированный подход

При некоторых обстоятельствах может быть желательно избегать sort, который unique/0 использует. Вот потоковое решение, использующее универсальный фильтр uniques/1, который не требует сортировки и имеет другие потенциальные преимущества, хотя его немного сложно определить, поскольку он не накладывает ограничений на поток.

def uniques(stream):
  foreach stream as $s ({};
     ($s|type) as $t
     | (if $t == "string" then $s else ($s|tostring) end) as $y
     | if .[$t][$y]
       then .emit = false
       else .emit = true | (.item = $s) | (.[$t][$y] = true)
       end;
     if .emit then .item else empty end );

При использовании uniques/1 достаточно небольшого изменения предыдущего решения:

uniques(.[]
 | select(.accountEnabled==true)
 | select(.assignedPlans[].service=="exchange" and .assignedPlans[].capabilityStatus=="Enabled")
 | .proxyAddresses[] )

...