【问题标题】:iterate through properties of multiple objects遍历多个对象的属性
【发布时间】:2021-02-15 16:16:40
【问题描述】:

我正在为我的应用开发过滤引擎。现在,我遇到了对象数据之间的迭代问题,我需要检查用户应用的过滤器数组。最终目标是返回匹配一个或多个过滤器选择的对象。

这是我的代码:

const Results = () => {
const [dataStored, setDataStored] = useState([])
const filterArray = useSelector(state => state.filterArray);
const doctors = useSelector(state => state.doctors);

// let doctorsResult = doctors.map(doctor => doctor.indexOf(filterArray) !== -1 )

// let doctorsResult = doctors.map(doctor => {for(let key of Object.keys(doctor)){
//     if (key === 'price' || key === 'gender' || key === 'language'){
//     // setDataStored([...dataStored, doctor[key]])
//     console.log("dataStored",doctor[key])}
//     //  return (dataStored.indexOf(filterArray) !== -1 )

// }})

let doctorsResult = doctors.map(doctor => {
    Object.keys(doctor).forEach((key) => {
        if (key === 'price' || key === 'gender' || key === 'language') {
            setDataStored([...dataStored, doctor[key]])
            console.log("dataStored", dataStored)
        }
        return (dataStored.indexOf(filterArray) !== -1)
    })
})

每个对象都有多个属性,但我只需要检查“价格、性别和语言”值。属性不相等,有些只是字符串,有些是数组。到目前为止,我已经能够使用 for..in 和 forEach 循环通过属性进行迭代。我的问题是,我无法比较和返回任何数据,因为它不是数组,因此 indexOf() 给了我一个错误。当我尝试setDataStored([...dataStored, doctor[key]]) 时,状态进入无限循环。

我对这一切都很陌生。如果有人有更好的方法来实现这一点,我将不胜感激。

编辑:

这是 filterArray 的形状 这是一个动态过滤器,开始是空的,然后被填充

【问题讨论】:

  • 您是否在寻找filter 而不是map?此外,对于那个dataStored,您可能只想在循环之前初始化它,然后push 对其进行初始化。不要为此使用状态挂钩。
  • filterArray的形状是什么?
  • @Bergi 在现场。在一次渲染期间,您将调用setDataStored n 次,其中 n 是匹配数。问题是,每次在第一次调用 setDataStored 后,都会使用陈旧的数据。这意味着每个渲染都只是将 一个 项添加到 dataStored,尽管做了所有这些工作。作为一般规则,如果您使用 useState 在组件中计算状态,您希望在备忘录或效果挂钩中进行计算。
  • 谢谢@Bergi,你说的很对。您的评论确实帮助我实现了我想要做的事情。感谢其余的 cmets,我意识到我也需要应用一个过滤器函数,这样我就可以返回一个返回 true 的对象数组。

标签: javascript arrays reactjs loops iteration


【解决方案1】:

所以你想从两个选择器中获取状态并做一些工作,然后返回结果?这是reselect 的完美问题类型。 Reselect 是一个帮助器,可让您记住有关状态选择器的昂贵计算。

https://github.com/reduxjs/reselect

这对你来说可能是这样的。

$ yarn add reselect

import React from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';

const filterArraySelector = (state) => state.filterArray;
const doctorsSelector = (state) => state.doctors;

const filteredDoctorsSelector = createSelector(doctorsSelector, filterArraySelector, (filterArray, doctors) => {
  return doctors.filter((doctor) => {
    return filterArray.all((key) => {
      // Do some comparison here, return true if you want to include the doctor in the results
      return doctor[key] !== undefined;
    });
  });
});

const Results = () => {
  const filteredDoctors = useSelector(filteredDoctorsSelector);

  return filteredDoctors.map((doctor) => <span>{doctor}</span>);
};

备选方案

无需使用createSelector 来记忆过滤,您可以在每次渲染时简单地过滤医生。像这样:

const Results = () => {
  const filterArray = useSelector((state) => state.filterArray);
  const doctors = useSelector((state) => state.doctors);

  const filteredDoctors = useMemo(
    () =>
      doctors.filter((doctor) => {
        return filterArray.all((key) => {
          // Do some comparison here, return true if you want to return the doctor
          return doctor[key] !== undefined;
        });
      }),
    [doctors, filterArray]
  );

  return filteredDoctors.map((doctor) => <span>{doctor}</span>);
};

更新:

给定一个 filterArray 的值,如下所示:

const filterArray = ['Female', 'English'];

我们可以更新过滤器函数以根据 filterArray 值测试 Objects 值。如果任何属性值与 filterArray 值匹配,那么我们可以将医生包含在生成的 filtersDoctors 列表中。

const Results = () => {
  const filterArray = useSelector((state) => state.filterArray);
  const doctors = useSelector((state) => state.doctors);

  const filteredDoctors = useMemo(() => {
    return doctors.filter((doctor) => {
      return filterArray.some((filter) => {
        return Object.values(doctor).some((value) => value === filter);
      });
    });
  }, [doctors, filterArray]);

  return filteredDoctors.map((doctor) => <span>{doctor}</span>);
};

更新:

聊天讨论后:

const Results = () => {
  const filterArray = useSelector((state) => state.filterArray);
  const doctors = useSelector((state) => state.doctors);

  const filteredDoctors = useMemo(() => {
    return doctors.filter((doctor) => {
      return filterArray.some((filter) => {
        return Object.values(doctor).some((value) => {
          // If the attribute value is an array
          if (Array.isArray(value)) {
            return value.some((value) => value === filter);
          }
          // If the attribute value is an object, get the values from the object
          if (typeof value === 'object') {
            return Object.values(value).some((value) => value === filter);
          }
          // By default, expect the value to be a string
          return value === filter;
        });
      });
    });
  }, [doctors, filterArray]);

  return filteredDoctors.map((doctor) => <span>{doctor}</span>);
};

【讨论】:

  • 我建议将 useMemo 用于替代情况,而不是在每次渲染时重新计算。我喜欢我们闪亮的新反应玩具。无论如何都很好的答案。
  • @Soupd'Campbells 这是一个很好的建议,我将更新我的代码 sn-p。
  • 不应该all 是some 吗? “最终目标是返回匹配一个或多个过滤器选择的对象。” 例如。 filterArray.some(key =&gt; doctor[key] !== undefined)
  • 感谢你们的 cmets 伙计们。我在这里学习新东西。 useMemo 或 reSelect 之间的主要区别是什么?我想到的是,reSelect 只有在两个 useSelector 完全比较后才会停止,而 useMemo 可以设置为在过滤期间的任何时间触发。对吗?
  • @3limin4t0r 可能,我不清楚 filterArray 的形状是什么,或者过滤的逻辑是什么。
猜你喜欢
  • 1970-01-01
  • 2012-01-08
  • 2014-08-09
  • 2016-05-12
  • 2021-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多