簡體   English   中英

React.js 對對象數組的搜索過濾

[英]React.js search filtration on the array of objects

我遇到了search問題。 它是前端搜索而不是遠程搜索,我使用react.js因為它是問題中的一個要求,並創建了一個名為App的組件。 我的任務是根據類型值顯示和突出顯示匹配的部分。

我會很感激的。 如果您為此提供了一個很好的解決方案。

讓我告訴你整個場景。 我將這個問題分為 3 個部分。

第 1 部分:數據的形狀是什么?

數據的形狀是這樣的:

src/data.js:

export default [
    {
        id: 1,
        name: 'Wordpress',
        list: [
            {
                id: 1,
                name: 'Best Mobile App Builder',
                slug: '/'
            },
            {
                id: 2,
                name: 'Best Wordpress Themes',
                slug: '/'
            },
            {
                id: 3,
                name: 'Best Website Creator',
                slug: '/'
            },
            {
                id: 4,
                name: 'Best Wordpress Builder',
                slug: '/'
            }
        ]
    },
    {
        id: 2,
        name: 'SaaS',
        list: [
            {
                id: 1,
                name: 'Appointment Scheduling Software',
                slug: '/'
            },
            {
                id: 2,
                name: 'Design Services',
                slug: '/'
            },
            {
                id: 3,
                name: 'Online Cloud Storage',
                slug: '/'
            },
            {
                id: 4,
                name: 'Remote PC Access',
                slug: '/'
            }
        ]
    },
];

注意

搜索 2 處房產

基本上這是我的過濾器 function。

src/filter.js:

import _ from 'lodash';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';

/**
 * Returns the new filtered array with highlighted parts.
 * @param data {Array<Object>} - The collection to iterate over.
 * @param inputValue {string} - The input value.
 * @return {Array} - Returns the new filtered array.
 */
export const filterByNames = (data, inputValue) => {
    // Create a dynamic regex expression object with ignore case sensitivity
    const re = new RegExp(_.escapeRegExp(inputValue), 'i');
    const results = data.filter((object) => {
        if (re.test(object.name)) {
            return true;
        } else {
            return object.list.some((item) => {
                if (re.test(item.name)) {
                    // Calculates the characters to highlight in text based on query
                    const matches = match(item.name, inputValue);
                    // Breaks the given text to parts based on matches.
                    // After that create a new property named `parts` and assign an array to it.
                    item['parts'] = parse(item.name, matches);
                    return true;
                } else {
                    return false;
                }
            });
        }
    });
    return results;
};

搜索工作正常,但面臨兩個主要問題。

  1. name屬性發生上述匹配時,它會停止並且不會更深入地進行 go。 嵌套列表name屬性也發生了同樣的事情。

  2. 當過濾發生在幕后時,我們通過添加一個名為parts的新屬性來改變原始數據,該屬性包含突出顯示的部分,它是一個對象數組。 但我不想改變原始數據,而是想返回包含parts屬性的新過濾數組。

看到這個。

整體搜索

工作演示

編輯搜索前端

第 2 部分:我使用哪些第三方庫進行過濾和突出顯示?

  • lodash字符串 function escapeRegExp用於轉義 RegExp 特殊字符。

  • autosuggest-highlight匹配function 以根據查詢計算要在文本中突出顯示的字符。

    之后,從同一個庫中解析function 幫助我們根據匹配將給定文本分解為parts 最后,它將返回一個帶有匹配字符串的對象數組並highlight boolean 標志。 所以我們很容易在 UI 上將突出顯示的parts加粗

第 3 部分:應用程序組件

import React, { useState } from 'react';
import { filterByNames } from './filter';
import data from './data';


/**
 * Return the JSX for the List
 * @param data {Array<Object>} - The collection to iterate over.
 * @return {null|*} - Returns the JSX or null.
 */
const renderList = (data) => {
  if (Array.isArray(data) && data.length > 0) {
    return data.map((object) => {
      return (
          <div key={object.id}>
            <h1>{object.name}</h1>
            <ul className="list">
              {object.list.map((item) => {
                return (
                    <li key={item.id}>
                      {item.parts ? (
                          <a href={item.slug}>
                            {item.parts.map((part, index) => (
                                <span
                                    key={index}
                                    style={{ fontWeight: part.highlight ? 700 : 400 }}
                                >
                          {part.text}
                        </span>
                            ))}
                          </a>
                      ) : (
                          <a href={item.slug}>{item.name}</a>
                      )}
                    </li>
                )
              })}
            </ul>
          </div>
      )
    })
  } else {
    return null
  }
};

// Main App Component
const App = () => {

  const [value, setValue] = useState('');

  const onChangeHandler = (event) => {
    const { target } = event;
    const val = target.value;
    setValue(val);
  };

  const results = !value ? data : filterByNames(data, value);
  
    return (
        <div className="demo">
          <input type="text" value={value} onChange={onChangeHandler}/>
          <div className="demo-result">
            { renderList(results) }
          </div>
        </div>
    );
    
};

export default App;

這是修改后的代碼。

export const filterByNames = (data, inputValue) => {
  // Create a dynamic regex expression object with ignore case sensitivity
  const re = new RegExp(_.escapeRegExp(inputValue), "i");
  const clonedData = _.cloneDeep(data);
  const results = clonedData.filter((object) => {
    return object.list.filter((item) => {
      if (re.test(item.name)) {
        // Calculates the characters to highlight in text based on query
        const matches = match(item.name, inputValue);
        // Breaks the given text to parts based on matches.
        // After that create a new property named `parts` and assign an array to it.
        item["parts"] = parse(item.name, matches);
        return true;
      } else {
        return false;
      }
    }).length > 0 || re.test(object.name);
  });
  return results;
};

分叉鏈接在這里。 https://codesandbox.io/s/search-frontend-forked-e3z55

這是解決了這兩個問題的代碼

export const filterByNames = (data, inputValue) => {
  // Create a dynamic regex expression object with ignore case sensitivity
  const re = new RegExp(_.escapeRegExp(inputValue), "i");
  // since we cannot directly mutate the data object, why not copy it here ? (or if the data is bigger and copying is also not an option then consider using two arrays of data, one for the mutation and one default maybe)
  let data_ = JSON.parse(JSON.stringify(data));
  // filter and return the newer copy of the object.
  const results = data_.filter((object) => {
    // since we need the highlighting in both cases, on top level, or even in nested level so create separate function for that.
    let highLightEm = (list) => {
      return object.list.some((item) => {
        if (re.test(item.name)) {
          // Calculates the characters to highlight in text based on query
          const matches = match(item.name, inputValue);
          // Breaks the given text to parts based on matches.
          // After that create a new property named `parts` and assign an array to it.
          item["parts"] = parse(item.name, matches);
          return true;
        } else {
          return false;
        }
      });
    };

    if (re.test(object.name)) {
      // check for the highlighting in the inner name
      highLightEm(object);
      return true;
    } else {
      return highLightEm(object);
    }
  });
  return results;
};

https://codesandbox.io/s/search-frontend-forked-kxui9?file=/src/filter.js

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM