通过复选框过滤对象数组

Posted

技术标签:

【中文标题】通过复选框过滤对象数组【英文标题】:Filter array of objects through checkboxes 【发布时间】:2021-08-18 05:28:46 【问题描述】:

这是React.js 过滤器问题,我被困在这里。问题是将电影的静态数据作为对象数组,我们必须根据不同复选框的选中状态过滤该数据。

静态数据:

export const data = [
    
        id: 1,
        title: 'Batman',
        rating: 1,
        genre: 'Action'
    ,
    
        id: 2,
        title: 'Superman',
        rating: 5,
        genre: 'Comedy'
    ,
    
        id: 3,
        title: 'Spiderman',
        rating: 3,
        genre: 'Thriller'
    ,
    
        id: 4,
        title: 'BananaMan',
        rating: 2,
        genre: 'Action'
    ,
    
        id: 5,
        title: 'OrangeMan',
        rating: 4,
        genre: 'Drama'
    
];

抽象的 UI 有下图,代表 2 种过滤器。

评分 类型

截图:

两个过滤器都在 UI 上呈现为复选框。因此,我创建了一个名为 Checkboxes 的组件,它将采用一个名为 list 的道具,它是一个对象数组。

这个list 代表唯一的ids 复选框,这将帮助我们跟踪复选框的checkedunchecked 状态。不同的复选框id 将保存在数组状态中。

const [checkedArray, setCheckedArray] = useState([]);

最后,我在名为App.js 的父组件内渲染了这个Checkboxes 组件两次。一个用于评分,另一个用于流派。

列表:

export const listCheckboxesRating = [
    
        id: 0,
        name: 'rating',
        label: 'Any Rating'
    ,
    
        id: 1,
        name: 'rating1',
        label: 'Rating 1'
    ,
    
        id: 2,
        name: 'rating2',
        label: 'Rating 2'
    ,
    
        id: 3,
        name: 'rating3',
        label: 'Rating 3'
    ,
    
        id: 4,
        name: 'rating4',
        label: 'Rating 4'
    ,
    
        id: 5,
        name: 'rating5',
        label: 'Rating 5'
    
];

export const listCheckboxesGenre = [
    
        id: 0,
        name: 'genre',
        label: 'Any Genre'
    ,
    
        id: 1,
        name: 'action',
        label: 'Action'
    ,
    
        id: 2,
        name: 'comedy',
        label: 'Comedy'
    ,
    
        id: 3,
        name: 'drama',
        label: 'Drama'
    ,
    
        id: 4,
        name: 'thriller',
        label: 'Thriller'
    
];

复选框组件:

import useState from 'react';
import handleToggle from '../../utils';

const Checkboxes = (list, handleFilters) => 
    const [checkedArray, setCheckedArray] = useState([]);

    const onChangeHandler = (checkboxId) => 
        const newState = handleToggle(checkboxId, checkedArray);
        setCheckedArray(newState);
        // Update this checked information into Parent Component
        handleFilters(newState);
    ;

    return list.map((item, index) => 
        return (
            <div key=index>
                <input
                    type="checkbox"
                    id=item.name
                    checked=checkedArray.indexOf(item.id) !== -1
                    onChange=() => onChangeHandler(item.id)
                />
                <label htmlFor=item.name>item.label</label>
            </div>
        );
    );
;

export default Checkboxes;

我通过名为handleFilters 的函数将这些复选框的整个状态传递给父组件App.js。可以看到这行代码。

handleFilters(newState);

App.js 组件正在保存这些复选框的状态,如下所示:

 const [selected, setSelected] = useState(
        rating: [],
        genre: []
    );

App.js 组件:

import useState from 'react';
import Checkboxes from './Checkboxes';
import data, listCheckboxesRating, listCheckboxesGenre from '../data';

const App = () => 
    const [movies, setMovies] = useState(data);
    const [selected, setSelected] = useState(
        rating: [],
        genre: []
    );

    /**
     * This function will perform the filtration process based on the key value.
     *
     * @param number[] checkboxState - It will take the final state of checkboxes
     * @param string key - It will help us to determine the type of filtration
     */
    const handleFilters = (checkboxState, key) => 
        const newFilters = ...selected;
        newFilters[key] = checkboxState;
        // Filtration process
        for (let key in newFilters) 
            if (
                newFilters.hasOwnProperty(key) &&
                Array.isArray(newFilters[key]) &&
                newFilters[key].length > 0
            ) 
                if (key === 'rating') 
                 else if (key === 'genre') 
                
            
        

        // Save the filtered movies and update the state.
        //  setMovies();
        setSelected(newFilters);
    ;

    return (
        <div>
            movies.map((movie) => (
                <div key=movie.id>
                    <div>Name: movie.title</div>
                    <div>Genre :movie.genre</div>
                    <div>Rating: movie.rating</div>
                    <hr />
                </div>
            ))

            <div className="row">
                <div className="col">
                    <h1>Filter by Rating</h1>
                    <Checkboxes
                        list=listCheckboxesRating
                        handleFilters=(checkboxState) =>
                            handleFilters(checkboxState, 'rating')
                        
                    />
                </div>

                <div className="col">
                    <h1>Filter by Genre</h1>
                    <Checkboxes
                        list=listCheckboxesGenre
                        handleFilters=(checkboxState) =>
                            handleFilters(checkboxState, 'genre')
                        
                    />
                </div>
            </div>
        </div>
    );
;

export default App;

两个过滤器的复选框checkedunchecked 状态工作正常,并且在父selected 状态中使用ids 正确更新,类似这样。

 const [selected, setSelected] = useState(
            rating: [1,2,3],
            genre: [2,4]
        );

但这是我被卡住的令人困惑的部分。如何映射这些复选框ids并根据这些ids过滤数据?

我在App.js 组件handleFilters(checkboxState, key) 中定义了一个函数,我想在其中迭代selected 状态以执行过滤过程,但我的脑子没有逻辑,我需要帮助。

工作演示:

repository link.

【问题讨论】:

【参考方案1】:

因此,我必须对您的代码进行一些更改才能解决正确的过滤问题:

    listCheckboxesRatinglistCheckboxesGenre 上都添加了value 键,用于过滤。
export const listCheckboxesGenre = [
  
    id: 0,
    name: 'genre',
    label: 'Any Genre',
    value: ''
  ,
  ...

    更新了onChangeHandler 以将value 传递回handleFilters 而不是id
const onChangeHandler = (checkboxId) => 
    // ...
    handleFilters(newState.map((id) => list[id].value));
;
    创建了几个过滤器函数来过滤电影。
const handleFilters = (checkboxState, key) => 
  const newFilters =  ...selected ;
  newFilters[key] = checkboxState;

  const hasFilters =
    newFilters.rating.length > 0 || newFilters.genre.length > 0;
  const filterRating = (module) =>
    newFilters.rating.includes(0) ||
    newFilters.rating.includes(module.rating);
  const filterGenre = (module) =>
    newFilters.genre.includes('') || newFilters.genre.includes(module.genre);

  const filteredMovies = data.filter(
    (m) => !hasFilters || filterRating(m) || filterGenre(m)
  );
  setMovies(filteredMovies);
  setSelected(newFilters);
;

工作演示:

【讨论】:

我检查了这段代码,但Genre 过滤不起作用。 我认为你是对的,我忘了第一个过滤器没有考虑流派,让我来解决这个问题。 @VenNilson 我用工作演示创建了您的代码和框的副本。 向这个列表添加值很棒。当我选择 Rating 2 和流派 action 时还有一件事,结果应该只显示 1 部电影,而不是在结果中显示 2 部电影。我很好奇这是什么原因? 哦,那基本上是因为我要使用OR logic,您可以尝试使用我在代码中留下注释的AND logic【参考方案2】:

我做了一些重构。

    使数据更加通用。
    export const listCheckboxesRating = 
      key: 'rating',
      data: [
        
          id: 1,
          name: 'rating1',
          label: 'Rating 1',
          value: 1
        ,
        ...
    
    重构 Checkbox 组件并仅传递数据。
    <Checkboxes
      list=listCheckboxesGenre
      handleFilters=handleFilters
    />
    使过滤过程具有价值和关键。
    // Filtration process
    let filteredMovies = data.filter((movie) => 
      for (let key in newFilters) 
        if (
          newFilters.hasOwnProperty(key) &&
          Array.isArray(newFilters[key]) &&
          newFilters[key].length > 0
        ) 
          if (
            newFilters[key].findIndex((value) => movie[key] === value) === -1
          ) 
            return false;
          
        
      
      return true;
    );

工作演示: https://codesandbox.io/s/react-filter-forked-gs2b2?file=/src/components/App.js

【讨论】:

以上是关于通过复选框过滤对象数组的主要内容,如果未能解决你的问题,请参考以下文章

根据包含的数组内容过滤对象数组。 Vanilla JS,lodash,其他一些与反应相关的技术?

根据 Int 数组过滤数组

React:使用多个复选框过滤数组列表

通过搜索嵌套对象属性过滤对象数组

如何通过来自另一个对象数组的数据过滤对象数组? [关闭]

通过数组filter方法过滤数组中对象