将对象数组减少为在所有对象共享的属性中找到的唯一字符串(javascript)

将对象数组减少为在所有对象共享的属性中找到的唯一字符串(javascript),javascript,arrays,foreach,Javascript,Arrays,Foreach,我编写了一个javascript函数,它成功地返回HTML文档中使用的所有唯一类的数组 const elements = document.getElementsByTagName('*'); let classesUsedInHtml = htmlCollection => { let uniqClassSelectors = []; Array.from(htmlCollection) .filter(element => element.classList.l

我编写了一个javascript函数,它成功地返回HTML文档中使用的所有唯一类的数组

const elements = document.getElementsByTagName('*');

let classesUsedInHtml = htmlCollection => {
  let uniqClassSelectors = [];
  Array.from(htmlCollection)
    .filter(element => element.classList.length > 0)
    .map(element =>
      element.classList.forEach(
        item =>
          uniqClassSelectors.includes(item)
            ? null
            : uniqClassSelectors.push(item)
      )
    );
  return uniqClassSelectors;
};

console.log(classesUsedInHtml(elements));
我想使用
Array.prototype.reduce()
重构此函数,并跳过定义变量
uniqClassSelectors
的需要,但无法使其工作

这是我的重构函数,它不起作用(返回未定义的
累加器)。在这方面,我经常被
reduce()
绊倒。为什么
累加器
未定义?我是否在
reduce()
函数中正确使用了
forEach()
?有人能给我指一下正确的方向吗?谢谢

let classesUsedInHtml = htmlCollection => {
  return Array.from(htmlCollection)
    .filter(element => element.classList.length > 0)
    .reduce((accumulator, element) => {
      return element.classList.forEach(
        classSelector =>
          accumulator.includes(classSelector)
            ? accumulator
            : accumulator.concat(classSelector)
      );
    }, []);
};


编辑:由于Nina Scholz注释,从相关函数中删除了不必要的
map()
我建议对最终的
domTokenList
使用另一个内部累加器,并将外部的
累加器
作为内部累加器
acc2
的起始值

let classesUsedInHtml = htmlCollection =>
    Array
        .from(htmlCollection)
        .filter(element => element.classList.length)
        .map(element => element.classList)
        .reduce((accumulator, domTokenList) =>
            domTokenList.reduce((acc2, classSelector) =>
                acc2.concat(acc2.includes(classSelector) ? [] : classSelector),
                accumulator
            ),
            []
        );
另一个解决方案是使用收集唯一的类选择器

let classesUsedInHtml = htmlCollection =>
    [...Array
        .from(htmlCollection)
        .filter(element => element.classList.length)
        .map(element => element.classList)
        .reduce((acc1, domTokenList) =>
            domTokenList.reduce((acc2, classSelector) => acc2.add(classSelector), acc1),
            new Set)
    ];

此解决方案的逐步指南。注释(//->)显示此步骤的结果,并将其传递到下一步

  • 使用以下命令将
    htmlCollection
    转换为数组:

  • 将每个元素添加到类数组中(再次展开):

  • 通过将子阵列展开到以下位置,将其展平:

  • 将数组转换为,并展开回数组,以获取唯一的类名:

    [...new Set([class, class, class, etc...])] // -> [class1, class2, class3, etc...]
    
  • const elements=document.getElementsByTagName('*');
    const classesUsedInHtml=(htmlCollection)=>
    […新集合(//获取唯一项并转换为数组
    [].concat(…//concat所有类的数组
    […htmlCollection]//将集合转换为数组
    .map(({classList})=>[…classList])//获取类的数组
    )
    ];
    const result=classesUsedInHtml(元素);
    控制台日志(结果)
    
    
    
    如果不获取返回值(在第一个代码中),为什么要映射?第二个不返回您想要的
    forEach
    。它只返回未定义的。很好地捕捉到了@NinaScholz。我将更新上面的问题函数。但这并不能解决根本问题。
    DOMTokenList
    不是一个数组,但它的原型中确实有
    forEach()
    方法。因此,如果您添加
    Array.from(domTokenList).reduce…
    ,您的代码就可以工作。在数组上使用另一个
    reduce()
    是个好主意。但是您知道如何使用
    forEach()
    ?正如我在评论中所写的那样,
    forEach
    不会返回要使用的内容。在这种情况下,您需要一个外部变量,您希望消除它。所以,要么使用
    地图
    ,要么使用reduce获取新值。谢谢,在我发布之前没有看到您的更新评论。哇!我知道我不应该用评论来表示“谢谢”,但我以前不知道
    Set
    。使用spread运算符时非常优雅,这正是我所寻求的方法。除了提供的注释之外,有人能解释整个解决方案中发生了什么吗?@BrianZ-我已经添加了一个解决方案的逐步分解。非常有用,您的说明从代码内部开始,然后流出。我从外到内阅读,然后迷路了。再次感谢!
    [htmlElement, htmlElement, etc...].map(({ classList }) => [...classList])) // -> [[class, class, etc...], [class, class, etc...], etc...]
    
    [].concat(...[[class, class, etc...], [class, class, etc...]], etc...) // -> [class, class, class, etc...]
    
    [...new Set([class, class, class, etc...])] // -> [class1, class2, class3, etc...]