ReactJS Как отфильтровать массив по нужным условиям?

Всем привет! Пытаюсь реализовать простую функцию поиска в проекте. Проект - список атрибутов. У меня есть массив этих элементов:

const selectedAttributesArray = [
{
attrType: "ЭЛЕМЕНТ",
code: "Teen",
id: 162,
links: [],
note: "string",
type: "protegrity",
},
{
attrType: "ЭЛЕМЕНТ",
code: "DE_unicode1",
id: 141,
links: [ 
{idElementConnect: 162, 
protectionFunc: 'DE1_string', 
unprotectionFunc: 'DE2_string', 
codeElementConnect: 'IK_HIVE'
},
],
note: "string",
type: "protegrity",
},
{
attrType: "ЭЛЕМЕНТ",
code: "R1_to_de",
id: 81,
links: [ 
{idElementConnect: 182, 
protectionFunc: 'ptyProtectStr', 
unprotectionFunc: 'ptyUnprotectStr', 
codeElementConnect: 'HIVE'}, 
{idElementConnect: null, 
protectionFunc: 'FUNCTION_FOR', 
unprotectionFunc: null, 
codeElementConnect: null}, 
{idElementConnect: 201, 
protectionFunc: 'ptyProtectStr', 
unprotectionFunc: 'ptyUnprotectStr', 
codeElementConnect: 'SPARK'},
],
note: "string1",
type: "protegrity",
}
]

Чтобы найти элемент массива есть инпут с отдельным стейтом searchValue (стейт находится в родительском компоненте):

export const SearchWidget = ({ searchValue, handleSearchQuery }) => {
    
  return (
    <div className={styles.mainContainer}>
        <Input
        placeholder='Найти атрибут...'
        value={searchValue}
        onChange={(e: string) => handleSearchQuery(e)} 
        />
    </div>
  )
}

и в итоге с помощью функции я фильтрую массив, чтобы уже из него мапить данные при рендеринге:

    export const DataResultCards = ({ selectedAttributesArray, searchValue, handleOpenSideBar }) => {
  
  const filteredAttributeList = selectedAttributesArray?.filter(attribute =>
    new RegExp(`${searchValue.toLowerCase().replace(/%/g, '.')}`).test(attribute.code.toLowerCase()),
  );

  return (
    <>
      {filteredAttributeList.length > 0 ? (
        filteredAttributeList?.map(attribute => {
          return <DataResultCard attribute={attribute} />;
        })
      ) : (
        <div>Ничего не найдено</div>
      )}
    </>
  );
};

Как должен осуществляться поиск НАПРИМЕР:

  1. при вводе в поиск "de" я получаю в качестве результата два объекта: карточку атрибута "DEunicode1" и "R1_tode" (нашли по названию атрибута)
  2. если я ввожу в поиск "hiv", то получу на экране карточки "DE_unicode1" (у него codeElementConnect: 'IK_HIVE') и "R1_to_de" (у него codeElementConnect: 'HIVE').
  3. при вводе "Str" результаты те же, потому что у них protectionFunc: 'DE1_string' и protectionFunc: 'ptyProtectStr'.

Код отдельной карточки атрибута:

export const DataResultCard = ({ attribute, handleOpenSideBar }) => {
  return (
    <Card
      style={{ width: 804, minHeight: 180, marginBottom: '5px' }}
    >
<Tag classes={{ tag: styles.typeTag }}>{attribute.attrType}</Tag>
        {attribute.links.length > 0
          ? attribute.links?.map(link => {
              if (link.codeElementConnect === null) {
                return null;
              } else {
                return (
                  <div onClick={() => handleOpenSideBar(link)}>
                    <Tag classes={{ tag: styles.connectedAttrTag }}>{link.codeElementConnect}</Tag>
                  </div>
                );
              }
            })
          : null}
<Text kind="h4b">{attribute.code}</Text>
<Text kind="textSb">Функции шифрования: </Text>
              {attribute.links?.map(link => {
                return (
                  <ul>
                    <li>
                      <ListEncryptionFunction link={link} attribute={attribute} />
                    </li>
                  </ul>
                );
              })}
</Card>
  );
};

Я пыталась переписать фильтр, чтобы и по массиву ссылок от атрибутов тоже можно было найти элемент по названию ссылки:

 const filteredAttributeList = selectedAttributesArray?.filter((attribute) => {
        attribute.links?.map((link) => {
          if (link.codeElementConnect !== null) {
            new RegExp(`${searchValue.toLowerCase().replace(/%/g, '.')}`).test(link.codeElementConnect.toLowerCase()),
          } else {
            new RegExp(`${searchValue.toLowerCase().replace(/%/g, '.')}`).test(attribute.code.toLowerCase()),
          }
        } 
          )
      }
    );

Но так поиск не работает. Как можно задать фильтр с несколькими условиями, чтобы найти элемент и по названию его ссылок?


Ответы (2 шт):

Автор решения: Oliver Patterson

Если я правильно понял задачу, и вам нужно искать либо по code либо по значению в объектах в links, то такой код будет верным.

const selectedAttributesArray = [
    {
        attrType: "ЭЛЕМЕНТ",
        code: "Teen",
        id: 162,
        links: [],
        note: "string",
        type: "protegrity",
    },
    {
        attrType: "ЭЛЕМЕНТ",
        code: "DE_unicode1",
        id: 141,
        links: [
            {
                idElementConnect: 162,
                protectionFunc: 'DE1_string',
                unprotectionFunc: 'DE2_string',
                codeElementConnect: 'IK_HIVE'
            },
        ],
        note: "string",
        type: "protegrity",
    },
    {
        attrType: "ЭЛЕМЕНТ",
        code: "R1_to_de",
        id: 81,
        links: [
            {
                idElementConnect: 182,
                protectionFunc: 'ptyProtectStr',
                unprotectionFunc: 'ptyUnprotectStr',
                codeElementConnect: 'HIVE'
            },
            {
                idElementConnect: null,
                protectionFunc: 'FUNCTION_FOR',
                unprotectionFunc: null,
                codeElementConnect: null
            },
            {
                idElementConnect: 201,
                protectionFunc: 'ptyProtectStr',
                unprotectionFunc: 'ptyUnprotectStr',
                codeElementConnect: 'SPARK'
            },
        ],
        note: "string1",
        type: "protegrity",
    }
];

const filteredAttributeList = useMemo(() =>
{
    const filtered = selectedAttributesArray.filter((attribute) =>
    {
        const searchValueReg = new RegExp(searchValue.replace(/%/g, '.'), "i");

        if (searchValueReg.test(attribute.code))
        {
                return true;
        }
        
        return attribute.links.some((link) => 
        {
            return Object.values(link).filter((linkValue) =>
            {
                return searchValueReg.test(linkValue);
            }).length > 0
        });
    });

    return filtered;
}, [ searchValue, selectedAttributesArray ]);
→ Ссылка
Автор решения: ksa

Как должен осуществляться поиск НАПРИМЕР: при вводе в поиск "de" я получаю в качестве результата два объекта: карточку атрибута "DEunicode1" и "R1_tode" (нашли по названию атрибута) если я ввожу в поиск "hiv", то получу на экране карточки "DE_unicode1" (у него codeElementConnect: 'IK_HIVE') и "R1_to_de" (у него codeElementConnect: 'HIVE'). при вводе "Str" результаты те же, потому что у них protectionFunc: 'DE1_string' и protectionFunc: 'ptyProtectStr'.

Предложу такой вариант фильтрации...

const arr = [
    {
        attrType: "ЭЛЕМЕНТ",
        code: "Teen",
        id: 162,
        links: [],
        note: "string",
        type: "protegrity",
    },
    {
        attrType: "ЭЛЕМЕНТ",
        code: "DE_unicode1",
        id: 141,
        links: [ 
            {
                idElementConnect: 162, 
                protectionFunc: 'DE1_string', 
                unprotectionFunc: 'DE2_string', 
                codeElementConnect: 'IK_HIVE'
            },
        ],
        note: "string",
        type: "protegrity",
    },
    {
        attrType: "ЭЛЕМЕНТ",
        code: "R1_to_de",
        id: 81,
        links: [ 
            {
                idElementConnect: 182, 
                protectionFunc: 'ptyProtectStr', 
                unprotectionFunc: 'ptyUnprotectStr', 
                codeElementConnect: 'HIVE'
            }, 
            {
                idElementConnect: null, 
                protectionFunc: 'FUNCTION_FOR', 
                unprotectionFunc: null, 
                codeElementConnect: null
            }, 
            {
                idElementConnect: 201, 
                protectionFunc: 'ptyProtectStr', 
                unprotectionFunc: 'ptyUnprotectStr', 
                codeElementConnect: 'SPARK'
            },
        ],
        note: "string1",
        type: "protegrity",
    }
]
const a = ['de', 'hiv', 'Str']
a.forEach(v => console.log(v, test(arr, v).map(o => o.code)))

//
function test(arr, txt = '') {
    const r = new RegExp(txt, 'i')
    const fn1 = v => r.test(v)
    const fn2 = o => Object.values(o).some(fn1)
    const fn3 = obj => r.test(obj.code) || obj.links.some(fn2)
    return arr.filter(fn3)
}

→ Ссылка