Список фильтров на основе другого результата запроса с помощью JMESPath

Наличие объекта, подобного приведенному ниже:

{
  "pick": "a",
  "elements": [
    {"id": "a", "label": "First"},
    {"id": "b", "label": "Second"}
  ]
}

как я могу получить элемент в списке elements, где id равно значению pick?

Я пытался что-то вроде:

elements[?id == pick]

Но, по-видимому, выражение справа от компаратора оценивается относительно объекта, проверяемого на соответствие моему фильтрующему выражению.

Как я могу достичь того, чего хочу? Если это невозможно из коробки, есть ли у вас какие-либо предложения о том, с чего мне начать расширять JMESPath? Благодарю вас!


person Victor    schedule 13.01.2020    source источник
comment
См. также: ссылка на github со ссылкой на эту конкретную проблему github.com/grofers/go-codon/wiki/   -  person dreftymac    schedule 19.06.2021


Ответы (1)


К сожалению, JMESPath не позволяет ссылаться на родительский элемент.

Чтобы обойти это ограничение, в этом простом случае вы можете:

  • прочитать атрибут pick в первом запросе,
  • создайте второй запрос, используя только что прочитанное значение,
  • прочитать требуемый контент во втором запросе.

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

pck = jmespath.search('pick', dct)
jmespath.search(f'elements[?id == `{pck}`]', dct)

где dct — исходный объект JSON.

Более сложный случай

Если у вас более сложный случай (например, много таких элементов с разными значениями pick в каждом случае), вам следует использовать другой инструмент.

Один довольно интересный вариант — использовать пакет Pandas.

Предположим, что ваш исходный словарь содержит:

dct = {
  "x1": {
    "pick": "a",
    "elements": [
      {"id": "a",    "label": "First_a"},
      {"id": "b",    "label": "Second_a"},
      {"id": "c",    "label": "Third_a"}
    ]
  },
  "x2": {
    "pick": "b",
    "elements": [
      {"id": "a",    "label": "First_b"},
      {"id": "b",    "label": "Second_b"},
      {"id": "c",    "label": "Third_b"}
    ]
  }
}

Первое, что нужно сделать, это преобразовать dct в Pandas DataFrame:

import pandas as pd
df = pd.DataFrame.from_dict(dct, orient='index')

Результат (распечатанный в "сокращенном" виде):

   pick                                           elements
x1    a  [{'id': 'a', 'label': 'First_a'}, {'id': 'b', ...
x2    b  [{'id': 'a', 'label': 'First_b'}, {'id': 'b', ...

Описание (если у вас нет опыта работы с Pandas):

  • x1, x2, ... - столбец индекса - значения взяты из ключей первого уровня в dct.
  • pick — столбец с (неудивительно) элементами pick,
  • elements - столбец с элементами (пока что каждая ячейка содержит весь список).

Эта фигура не очень полезна, поэтому давайте расчленим столбец elements:

df = df.explode('elements')

Теперь df содержит:

   pick                          elements
x1    a   {'id': 'a', 'label': 'First_a'}
x1    a  {'id': 'b', 'label': 'Second_a'}
x1    a   {'id': 'c', 'label': 'Third_a'}
x2    b   {'id': 'a', 'label': 'First_b'}
x2    b  {'id': 'b', 'label': 'Second_b'}
x2    b   {'id': 'c', 'label': 'Third_b'}

Эта форма ближе к тому, что нам нужно: каждая исходная строка была разбита на пару строк, каждая с отдельным элементом из исходного списка.

Есть еще одна вещь, которую нужно сделать, т. е. создать столбец, содержащий значения id, которые позже будут сравниваться со столбцом pick. Для этого запустите:

df['id'] = df.elements.apply(lambda dct: dct['id'])

Теперь df содержит:

   pick                          elements id
x1    a   {'id': 'a', 'label': 'First_a'}  a
x1    a  {'id': 'b', 'label': 'Second_a'}  b
x1    a   {'id': 'c', 'label': 'Third_a'}  c
x2    b   {'id': 'a', 'label': 'First_b'}  a
x2    b  {'id': 'b', 'label': 'Second_b'}  b
x2    b   {'id': 'c', 'label': 'Third_b'}  c

А для получения конечного результата следует:

  • выберите строки со столбцом pick == id,
  • взять только столбец elements (вместе с ключевым столбцом, но эту деталь Pandas дает вам прямо из коробки).

Код для этого:

df.query('pick == id').elements

давая:

x1     {'id': 'a', 'label': 'First_a'}
x2    {'id': 'b', 'label': 'Second_b'}

На языке Pandas это серия (скажем, список, в котором каждый элемент «помечен» индексом.

Теперь вы можете преобразовать его в словарь или что угодно.

person Valdi_Bo    schedule 26.04.2020
comment
Большое спасибо за ваш ответ! Я собираюсь принять это, так как информация внутри кажется достоверной и полезной. Однако, поскольку я задал вопрос, я написал собственное решение. - person Victor; 27.04.2020