Поиск по вложенному массиву. TS
Нужно реализовать функцию, которая на вход принимает два массива и фильтрует первый согласно второму по заданным правилам.
Примеры:
Массив, который нужно отфильтровать
const response = [
{
name: {
first: 'TestFirst',
last: 'TestLast2',
},
},
goal: 'goal_1',
isTrue: true,
},
{
name: {
first: 'TestFirst2',
last: 'TestLast2',
},
goal: 'goal_2',
isTrue: true,
},
];
Массив с фильтрами
const filter = [
{
property: 'name.first',
in_values: ['TestFirst', 'TestFirst2'],
},
{
property: 'name.last',
not_in_values: ['TestLast2'],
},
{
property: 'goal',
in_values: ['goal_1', 'goal_2'],
},
{
property: 'isTrue',
in_values: [true],
},
];
Результирующий массив
const result = [
{
name: {
first: 'Test',
last: 'Test',
},
goal: 'goal_1',
isTrue: true,
},
];
property - поле, которое нужно проверить.
in_values - массив значений. Если есть хоть одно совпадение, тогда запись попадает в результирующий массив
not_in_values - массив значений. Если есть хоть одно совпадение, тогда запись НЕ попадает в результирующий массив
Если все "правила" фильтрации были применены успешно, тогда запись попадает в результирующий массив.
Вложенность может быть любая. Требуется универсальное решение. В response могут попасть объекты не только из примера. Внутри массива объекты одинаковой структуры.
Ответы (1 шт):
type Filter = {
/** Путь к проверяемому полю, разделенный точками (path.to.field) */
property: string;
/** Поле должно содержать хотя бы одно из значений */
in_values?: any[];
/** Поле не должно содержать ни одного из значений */
not_in_values?: any[];
};
const filters: Filter[] = [
{
property: 'name.test.foo',
in_values: ['bar'],
},
{
property: 'name.first',
in_values: ['TestFirst1', 'TestFirst2'],
},
{
property: 'name.last',
not_in_values: ['TestLast2'],
},
{
property: 'goal',
in_values: ['goal_1', 'goal_2'],
},
{
property: 'isTrue',
in_values: [true],
},
];
const response = [
{
name: {
first: 'TestFirst1',
last: 'TestLast1',
test: {
foo: 'bar',
}
},
goal: 'goal_1',
isTrue: true,
},
{
name: {
first: 'TestFirst2',
last: 'TestLast2',
test: {
foo: 'foo',
}
},
goal: 'goal_2',
isTrue: true,
},
];
/**
* Получение вложенного поля по указанному пути
* @example getNestedPropertyValue({ test: { foo: "bar" } }, "test.foo"); // "bar"
*/
function getNestedPropertyValue(object: any, path: string): any {
for (const field of path.split('.')) {
object = object[field];
}
return object;
}
/** Содержится ли значение value в указанном массиве values */
function getIsInValues(value: any, values: any[]) {
return values.includes(value);
}
/** Проходит ли фильтрацию указанное значение */
function getIsAvailableItem(value: any, filter: Filter) {
if (filter.in_values && !getIsInValues(value, filter.in_values)) {
return false;
}
if (filter.not_in_values && getIsInValues(value, filter.not_in_values)) {
return false;
}
return true;
}
/** Фильтрация массива */
function filter<Object>(array: Object[], filters: Filter[]): Object[] {
const filteredArray: Object[] = [];
// Проходим по массиву проверяемых значений
itemsLoop:
for (const item of array) {
// Проходим по массиву фильтров
for (const filter of filters) {
// Получаем значение целевого поля по пути фильтра
const targetFieldValue = getNestedPropertyValue(item, filter.property);
// Пропускаем текущий элемент если значение целевого поля не проходит фильтр
if (!getIsAvailableItem(targetFieldValue, filter)) {
continue itemsLoop;
}
}
// Добавляем элемент в массив результатов, так как он успешно прошел все фильтры
filteredArray.push(item);
}
return filteredArray;
}
console.log(filter(response, filters));
Важно!
Данный пример предполагает что в каждом из проверяемых элементов в массиве будут присутствовать все поля, указанные в property фильтров.
Если Вам нужно считать отсутствие полей как отрицательный результат фильтрации, замените указанные функции на эти.
function getNestedPropertyValue(object: any, path: string): any {
for (const field of path.split('.')) {
object = object[field];
if (object === undefined) {
return undefined;
}
}
return object;
}
function filter<Object>(array: Object[], filters: Filter[]): Object[] {
const filteredArray: Object[] = [];
// Проходим по массиву проверяемых значений
itemsLoop:
for (const item of array) {
// Проходим по массиву фильтров
for (const filter of filters) {
// Получаем значение целевого поля по пути фильтра
const targetFieldValue = getNestedPropertyValue(item, filter.property);
// Пропускаем текущий элемент если значение не найдено
if (targetFieldValue === undefined) {
continue itemsLoop;
}
// Пропускаем текущий элемент если значение целевого поля не проходит фильтр
if (!getIsAvailableItem(targetFieldValue, filter)) {
continue itemsLoop;
}
}
// Добавляем элемент в массив результатов, так как он успешно прошел все фильтры
filteredArray.push(item);
}
return filteredArray;
}