Вопрос или проблема
У меня есть структура JSON, похожая на следующую
{
"a": {"b": "значение"},
"c": {"b": "значение"},
"d": {"b": "значение"},
"a": {"e": {"b": ["значение"]}}
}
Ожидаемый ответ – 4
. b
может находиться где угодно в древовидной структуре.
Я хочу узнать (для тестирования), сколько экземпляров b
у меня есть. Google говорит мне использовать JSON.stringify
, а затем работать со строкой. В XML я мог бы использовать XPath, чтобы подсчитать количество элементов. Есть ли способ в JS/JSON, чтобы сделать это, кроме как перебирать ключи каждого поля?
Есть: JSON.stringify
ваш объект с функцией-заменителем, использование которой превращает его из “генератора строк” в “функцию-утилиту, которая делает все, что вы хотите с любыми парами ключ/значение, проходя по древу объекта”:
const data = {
"a": {"b": "значение"},
"c": {"b": "значение"},
"d": {"b": "значение"},
"a": {"e": {"b": ["значение"]}}
};
let count = 0;
JSON.stringify(data, (key, value) => {
if (key === `b`) count++;
return value;
});
console.log(`всего ${count} экземпляров 'b'`);
Но отметьте, что ответ будет три, а не четыре, потому что в JS нет объектов с дублирующимися ключами, и так JSON с дублирующимися ключами следует тем же правилам: только последний экземпляр ключа является действительным, и что бы ни создавало JSON, который вы показываете, должно быть исправлено, чтобы не выдавать (частичный) абсурд =)
</div><div class="s-prose js-post-body" itemprop="text">
Этот ответ учитывает, что дублирующиеся ключи являются частью проблемы.
Первым делом, у вас есть проблема с вашим JSON – он недействителен. Примерный код для рекурсивной проверки ключей выглядел бы так:
function countKeys(searchKey, obj) {
let counter = 0;
for (const key in obj) {
if (key === searchKey) {
counter++;
}
if (typeof obj[key] === 'object') {
counter += countKeys(searchKey, obj[key]);
}
}
return counter;
}
это вернёт 3
для "b"
, просто потому что только один из ключей “a” будет учтён.
Даже если вы используете regex
с JSON.stringify, строка, возвращаемая функцией, даст только действительные строки JSON, что означает, что вы также потеряете дублирующиеся ключи.
console.log((JSON.stringify(keys).match(/"b":/g) || []));
// выводит Array(3) [ '"b":', '"b":', '"b":' ]
Возможные решения:
- Убедитесь, что ваш сервер возвращает действительный JSON;
- Сохраните JSON как строку (если это ответ на запрос, например, не используйте .json() на нем) затем используйте regex.
Сообщите мне, если это поможет!
</div><div class="s-prose js-post-body" itemprop="text">
Еще одно решение – рекурсивно получить все ключи и затем отфильтровать их по символу, который вы ищете.
const data = {
"a": {"b": "значение"},
"c": {"b": "значение"},
"d": {"b": "значение"},
"x": {"e": {"b": ["значение"] } }
};
const getKeys = (obj) => Object.keys(obj).reduce(
(acc, key) => typeof obj[key] === 'object'
? [ ...acc, key, ...getKeys(obj[key]) ]
: [ ...acc, key ], []
)
const result = getKeys(data).filter(k => k === 'b')?.length || 0;
console.log(`Найдено ${result}x 'b'`);
Ответ или решение
Когда речь идет о подсчете экземпляров ключа в структуре JSON, важно учитывать, что правильная работа с данными JSON начинается с валидного формата. Прежде всего, приведенная структура JSON, содержащая дублирующиеся ключи, является невалидной, так как в JavaScript объекты не могут иметь одинаковые ключи. Как правило, при наличии дублирующихся ключей в объекте сохраняется только последний экземпляр.
Проблема с невалидным JSON
Исходный пример:
{
"a": {"b": "value"},
"c": {"b": "value"},
"d": {"b": "value"},
"a": {"e": {"b": ["value"]}}
}
В этом JSON ключ "a"
определяется несколько раз, что является некорректным. JSON с дублирующимися ключами приводит к тому, что значения всех дубликатов, кроме последнего, теряются.
Подсчет экземпляров ключа в JSON
Если мы рассмотрим, как подсчитать экземпляры ключа, нам необходимо воспользоваться рекурсионным подходом, так как JSON может содержать многоуровневые структуры. Мы можем написать функцию, которая рекурсивно проходит по всем уровням JSON и считает количество вхождений искомого ключа.
Пример реализации функции
Вот простой пример на JavaScript, который показывает, как можно рекурсивно подсчитать количество вхождений определенного ключа:
function countKeys(searchKey, obj) {
let counter = 0;
for (const key in obj) {
if (key === searchKey) {
counter++;
}
if (obj[key] !== null && typeof obj[key] === 'object') {
counter += countKeys(searchKey, obj[key]);
}
}
return counter;
}
const data = {
"a": {"b": "value"},
"c": {"b": "value"},
"d": {"b": "value"},
"x": {"e": {"b": ["value"]}}
};
const count = countKeys('b', data);
console.log(`Найдено ${count} экземпляров 'b'`);
Использование JSON.stringify
Другой подход может заключаться в использовании метода JSON.stringify()
с функцией замены, чтобы пройтись по объекту. Однако стоит помнить, что это позволит работать только с последним экземпляром каждого ключа.
let count = 0;
JSON.stringify(data, (key, value) => {
if (key === 'b') count++;
return value;
});
console.log(`Найдено ${count} экземпляров 'b'`);
Тем не менее, этот подход не будет работать должным образом в случае дублирующихся ключей, так как JSON.stringify
создает валидную JSON-строку, которая удаляет все дубликаты.
Рекомендации
- Исправьте структуру JSON. Обязательно убедитесь, что на вашей стороне сервера возвращается валидный JSON без дубликатов ключей.
- Используйте строку JSON (если это ответ на запрос), и применяйте регулярные выражения для поиска ключей, но этот метод менее оптимален.
- Рассмотрите возможность использования рекурсивных функций, которые могут учитывать иерархию объектов.
Заключение
При работе с JSON всегда важно следить за его валидностью и структурой. Если необходимо подсчитать количество экземпляров конкретного ключа, надёжным и безопасным способом будет использование рекурсивных функций, которые могут обойти вложенные объекты и правильно подсчитать все вхождения искомого ключа.