【问题标题】:Reduce JavaScrip Array of Objects based on boolean property value根据布尔属性值减少 JavaScript 对象数组
【发布时间】:2021-01-30 18:39:00
【问题描述】:

我正在尝试将视频信息中的以下数据减少到自定义对象结构中,该结构删除“标签”键重复项,但同时保留所有遇到的可用功能。

初始数据如下所示:

[
  {
    "label": "4320p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "2160p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "2160p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "1440p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "1440p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "1080p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "1080p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "1080p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "1080p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "480p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "480p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "480p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "480p",
    "features": {
      "HDR": true,
      "60fps": false
    }
  },
  {
    "label": "360p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "360p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "360p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "360p",
    "features": {
      "HDR": true,
      "60fps": false
    }
  },
  {
    "label": "240p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "240p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "240p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "240p",
    "features": {
      "HDR": true,
      "60fps": false
    }
  },
  {
    "label": "144p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "144p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "144p",
    "features": {
      "HDR": false,
      "60fps": false
    }
  },
  {
    "label": "144p",
    "features": {
      "HDR": true,
      "60fps": false
    }
  }
]

如您所见,单个分辨率标签有许多对象,但其中一些具有 HDR,而另一些具有 60fps,而另一些可能一个都没有,或者两者都没有。

我想要做的是用下面的 reduce 函数来减少这个数组。 假设resolutions就是上面的Object:

resolutions.reduce((unique, o) => {
    if (!unique.some((obj) => obj.label === o.label)) {
      unique.push(o);
    }
    return unique;
  }, []);

这给了我以下结构:

[
  {
    "label": "4320p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "2160p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "1440p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "1080p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "720p",
    "features": {
      "HDR": false,
      "60fps": true
    }
  },
  {
    "label": "480p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "360p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "240p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  },
  {
    "label": "144p",
    "features": {
      "HDR": true,
      "60fps": true
    }
  }
]

但是,如果您仔细观察,您会发现在 reduce 操作期间,一些布尔值与每个唯一标签键的第一个元素值合并,我最终需要启用真正的功能,即使其中只有一个是真的。 2160p 标签是一个很好的例子,其中实际上有一个具有 HDR: true 的对象,但最后我只是将它作为 false

您对如何使用现代 JavaScript 管理这种情况有任何想法吗?

【问题讨论】:

    标签: javascript object boolean reduce


    【解决方案1】:

    如果您想保持您的方法,您可以使用以下方法。您需要扩展if 语句,以防您有偏好应保留哪个元素,以防有两个元素,一个将 HDR 设置为 true,另一个将 60fps 设置为 true(在这种情况下使用此版本将选择第一个发现的分辨率):

    const resolutions = [{
        "label": "4320p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "2160p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "2160p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "1440p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "1440p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "1080p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "1080p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "1080p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "1080p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": false,
          "60fps": true
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "720p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "480p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "480p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "480p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "480p",
        "features": {
          "HDR": true,
          "60fps": false
        }
      },
      {
        "label": "360p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "360p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "360p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "360p",
        "features": {
          "HDR": true,
          "60fps": false
        }
      },
      {
        "label": "240p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "240p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "240p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "240p",
        "features": {
          "HDR": true,
          "60fps": false
        }
      },
      {
        "label": "144p",
        "features": {
          "HDR": true,
          "60fps": true
        }
      },
      {
        "label": "144p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "144p",
        "features": {
          "HDR": false,
          "60fps": false
        }
      },
      {
        "label": "144p",
        "features": {
          "HDR": true,
          "60fps": false
        }
      }
    ]
    
    
    const result = resolutions.reduce((unique, o) => {
      const uniqueResolution = unique.find((resolution) => resolution.label === o.label)
    
      if (!uniqueResolution) {
        return unique.concat(o)
      } else if (uniqueResolution.features.HDR && uniqueResolution.features['60fps']) {
        return unique
      } else if (o.features.HDR && o.features['60fps']) {
        // swap element since there is a better one
        return unique.map((resolution) => {
          if (resolution.label === o.label) return o
          return resolution
        })
      }
    
      return unique
    }, [])
    
    console.log(result)

    【讨论】:

      【解决方案2】:

      如果您首先使用label 并在每次迭代中按label 分组,会更容易:

      • 如果label 尚未在acc 中,则添加第一个对象(当前项)
      • 否则,更新已存储类别的特征,如果其中一个特征不可用并且该类别中的新当前项目具有该特征,则将其设置为true

      最后,返回具有合并特征的对象的分组列表:

      const resolutions = [
        { "label": "4320p", "features": { "HDR": false, "60fps": true } },
        { "label": "2160p", "features": { "HDR": false, "60fps": true } },
        { "label": "2160p", "features": { "HDR": true, "60fps": true } },
        { "label": "1440p", "features": { "HDR": false, "60fps": true } },
        { "label": "1440p", "features": { "HDR": true, "60fps": true } },
        { "label": "1080p", "features": { "HDR": false, "60fps": true } },
        { "label": "1080p", "features": { "HDR": true, "60fps": true } },
        { "label": "1080p", "features": { "HDR": false, "60fps": true } },
        { "label": "1080p", "features": { "HDR": true, "60fps": true } },
        { "label": "720p", "features": { "HDR": false, "60fps": true } },
        { "label": "720p", "features": { "HDR": true, "60fps": true } },
        { "label": "720p", "features": { "HDR": false, "60fps": true } },
        { "label": "720p", "features": { "HDR": false, "60fps": false } },
        { "label": "720p", "features": { "HDR": true, "60fps": true } },
        { "label": "720p", "features": { "HDR": false, "60fps": false } },
        { "label": "480p", "features": { "HDR": true, "60fps": true } },
        { "label": "480p", "features": { "HDR": false, "60fps": false } },
        { "label": "480p", "features": { "HDR": false, "60fps": false } },
        { "label": "480p", "features": { "HDR": true, "60fps": false } },
        { "label": "360p", "features": { "HDR": true, "60fps": true } },
        { "label": "360p", "features": { "HDR": false, "60fps": false } },
        { "label": "360p", "features": { "HDR": false, "60fps": false } },
        { "label": "360p", "features": { "HDR": true, "60fps": false } },
        { "label": "240p", "features": { "HDR": true, "60fps": true } },
        { "label": "240p", "features": { "HDR": false, "60fps": false } },
        { "label": "240p", "features": { "HDR": false, "60fps": false } },
        { "label": "240p", "features": { "HDR": true, "60fps": false } },
        { "label": "144p", "features": { "HDR": true, "60fps": true } },
        { "label": "144p", "features": { "HDR": false, "60fps": false } },
        { "label": "144p", "features": { "HDR": false, "60fps": false } },
        { "label": "144p", "features": { "HDR": true, "60fps": false } }
      ];
      
      // update category's features if any is not available yet, and the new object has it
      const _getUpdatedFeatures = (currentFeatures={}, newFeatures={}) => {
        const updatedFeatures = {...currentFeatures};
        for (let [feature, currentlyAvailable] of Object.entries(currentFeatures)) {
          if(!currentlyAvailable && newFeatures[feature]===true) {
            updatedFeatures[feature] = true;
          }
        }
        return updatedFeatures;
      }
      
      // group by label and merge features availability
      const res = Object.values(resolutions.reduce((acc, item) => {
        const { label } = item;
        const prev = acc[label];
        if(!prev) 
          acc[label] = item;
        else 
          acc[label] = { ...prev, features: _getUpdatedFeatures(prev.features, item.features) };
        return acc;
      }, {}));
      
      console.log(res);

      【讨论】:

      • 有趣的解决方案,它有效,只是想知道在reduce() 函数中是否有更简洁的方法。
      • 您能否进一步说明这里的“简洁”是什么意思?更新功能的想法可以在任何类型的循环中完成,但为了清楚起见,我这样做了
      • 只是没有分隔的_getUpdatedFeatures() 函数,我一直在阅读reduce() 函数here 的文档,但参数回调中有一些我无法完全理解的概念,但我想想可能会派上用场。无论如何,它现在可以正常工作,我感谢你。
      猜你喜欢
      • 2018-04-15
      • 1970-01-01
      • 2017-03-19
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 2020-03-25
      相关资源
      最近更新 更多