【问题标题】:Easy way to check if array of objects exists in array of objects before setting state React JS在设置状态 React JS 之前检查对象数组是否存在于对象数组中的简单方法
【发布时间】:2020-01-25 05:17:31
【问题描述】:

考虑以下几点:

  this.setState({
    validations: [...this.state.validations, ...validations]
  }, () => {
    // ... Do something
  });

我在做什么:[...this.state.validations, ...validations] 验证是一个对象数组。

例如:

// validation
[{message: 'hello', isError: true, fieldId: 87}, ....] 

基本上每次我们遇到验证错误时,我们都会将对象数组设置为一个数组,如上所示。

问题是有时会添加重复的对象。重复我的意思是在消息部分,所以this.state.validations 可能看起来像:

[
  {message: 'hello', isError: true, fieldId: 87}, 
  {message: 'hello', isError: true, fieldId: 87}, 
  {message: 'hello', isError: true, fieldId: 87}, 
  ...
]

我想做的是根据 validations 中的消息和 fieldId 过滤 this.state.validations 并说:

 // If this message doesn't exist for this field id, set it.

是否有一种简单的非混乱方式可以使用过滤器或其他东西来循环状态验证和比较 fieldId 和消息的验证,如果它不存在:添加它?

【问题讨论】:

    标签: javascript arrays reactjs react-state-management


    【解决方案1】:

    您可以从现有的状态验证数组中创建一个Set 的“键”,其中每个键都是message|fieldId 形式的字符串。然后,您可以根据之前创建的集合中是否存在新验证消息的键,从validations 中过滤掉元素来创建新数组:

    const stateValidations = [
      {message: 'hello', isError: true, fieldId: 87},
      {message: 'world', isError: true, fieldId: 87},
      {message: 'hi', isError: true, fieldId: 88}
    ];
    const validations = [
      {message: 'hello', isError: true, fieldId: 87},
      {message: 'there', isError: true, fieldId: 88}
    ];
    
    const messageKey = ({message, fieldId}) => `${message}|${fieldId}`;
    const existing = new Set(stateValidations.map(messageKey));
    
    const res = [...stateValidations, ...validations.filter(v => !existing.has(messageKey(v)))];
    console.log(res);

    【讨论】:

    • 致未来的读者:此解决方案展示了创建Set 并使用它来过滤验证。这是执行简单任务的一种不同但迂回的方式,涉及创建Set 的不必要的内存分配。如果使用Set,那么最好将状态验证更改为Set 而不是数组。
    • @Nikhil 我不确定你为什么使用“环形交叉路口”和“不必要的”这两个词。这种方法具有 O(n + m) 运行时间,其中权衡是集合的额外空间,而使用 find 会导致 O(nm) 运行时间(尽管实际情况中的对象数量肯定会不会) t 大到足以产生影响)。我也不确定通过将状态验证更改为 Set 会完成什么。
    • 当我们将Set.has() 操作视为常数时间O(1) 时,此方法具有O(n + m) 时间复杂度。尽管通常是这种情况,但 ECMA 规范要求实现为linear time O(n),这使您的时间复杂度为O(mn)。您可以在jsperf 中看到比较,其中find() 优于此实现。除此之外,此解决方案需要为Set 提供额外空间。这就是使用这些术语的原因。
    • 我建议将状态验证更改为 Set 减少了检查重复项的代码行,并具有高效的 O(n) 时间复杂度,还突出了 Set 的语义概念本质上是唯一的,并且是在这种情况下是适当的数据结构。
    • @Nikhil 您误引/误解了答案。根据规范,它们必须在 次线性时间 内实现:ecma-international.org/ecma-262/6.0/index.html#sec-set-objects。另请注意,更好的运行时间意味着它“最终”会更快,因此您的输入大小必须足够大才能看到差异。最后,如果对象“看起来相等”但实际上是内存中的不同对象,has 检查将失败。这就是为什么您不能直接从状态验证创建Set,因为has 检查将失败。
    【解决方案2】:

    您可以使用Array.find() 验证每个验证是否已存在,如果不存在则仅添加到状态验证中。

    var stateValidations = [
      {message: 'hello', isError: true, fieldId: 87}, 
      {message: 'hello', isError: true, fieldId: 42}, 
      {message: 'error', isError: true, fieldId: 95}, 
    ];
    
    var validations = [
      {message: 'hello', isError: true, fieldId: 42}, // Already exists.
      {message: 'hello', isError: true, fieldId: 101} // New item.
    ];
    
    // Check if each validation already exists, and only add if doesn't exist.
    validations.forEach(validation => {
      const found = stateValidations.find(item => item.message === validation.message 
        && item.fieldId === validation.fieldId);
    
      if (!found) {
        // If not found, add validation.
        stateValidations.push(validation);
      }
    });
    
    console.log(JSON.stringify(stateValidations));

    【讨论】:

      【解决方案3】:

      嗯,首先我们需要解决一些问题。 setState 是异步的,因此调用 setState 并传入一个取决于前一个状态的值是一个潜在的陷阱。

      所以不要这样做: this.setState({ value: ...this.state.value });

      我们需要将 setState 与更新函数一起使用,如下所示: this.setState(prevState => ({ value: ...prevState.value }));

      接下来,让我们通过在 prevState.validations 中找到匹配项来过滤新验证来解决您的问题。

          this.setState(prevState => {
            let toAdd = validations.filter(
              item =>
                undefined ===
                prevState.validations.find(
                  prevItem => item.message === prevItem.message && item.fieldId === prevItem.fieldId
                )
            );
            return [...prevState.validations, ...toAdd];
          });
      

      【讨论】:

      • 您必须将过滤条件更改为不包含已存在的项目。现在情况正好相反。
      • @Nikhil 啊,是的!谢谢你抓住它!我已经更新了代码。
      【解决方案4】:

      将验证构造为对象而不是数组的更好方法之一

      例如

      validations: { [fieldId1]: [{ message: 'hello', isError: true }, { message: 'bye', isError: true}], [fieldId2]: [{ message: 'hello', isError: true }], }

      使用这种结构,您可以通过一个字段 Id 清楚地看到所有验证都存在。

      现在要确定是否已在 fieldId 上进行了验证,您可以执行以下操作:

      if (validations[fieldId].filter(v => v.message === 'your message').length === 0) { this.setState({ validations: { ...validations, [fieldId]: [...validations[fieldId], { message: 'your message', isError: true }] } }) }

      这样,当你需要读取特定fieldId的所有验证时,你可以直接说:

      this.state.validations[fieldId]

      而不是再次过滤 fieldId 上的数组。

      【讨论】:

        【解决方案5】:

        解决方法很简单,

        1. 找出一件物品的独特之处
        2. 遍历现有项目,如果不重复则添加。如果重复则使用新项目并缩短新数组以提高性能
        3. 添加所有剩余项目
        const stateValidations = [
          {message: 'hello', isError: true, fieldId: 85},
          {message: 'world', isError: true, fieldId: 86},
          {message: 'hi', isError: true, fieldId: 88}
        ];
        const validations = [
          {message: 'hello', isError: true, fieldId: 87},
          {message: 'there', isError: true, fieldId: 88}
        ];
        
        let newValidations = stateValidations.reduce((acc, curr)=>{
          let haveNewvalidation = validations.findIndex((v)=>v.fieldId===curr.fieldId);
          if(haveNewvalidation !=-1){
            acc.push(validations[haveNewvalidation]);
            // this will improve findIndex performance
            validations.splice(haveNewvalidation,1);
        
          } else  {
            acc.push(curr);
          }
          return acc;
        }, [])
        
        // push remaining validations
        newValidations.push(...validations)
        
        console.log(newValidations)```
        
        Demo: https://repl.it/@abhirathore2006/mergeTwoArrays 
        
        

        【讨论】:

          猜你喜欢
          • 2023-01-18
          • 1970-01-01
          • 2019-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-11
          • 2020-11-30
          • 2019-07-23
          相关资源
          最近更新 更多