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

时间:2017-09-23 16:03:38

标签: javascript arrays foreach

我编写了一个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,但无法使其正常工作。

这是我的重构函数不起作用(返回accumulator is undefined)。在这方面,我常常被reduce()绊倒。为什么accumulator未定义?我没有在forEach()函数中正确使用reduce()吗?任何人都能指出我在正确的方向吗?谢谢!

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()

2 个答案:

答案 0 :(得分:2)

我建议使用另一个内部累加器作为最终domTokenList,并将外部accumulator作为内部累加器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
            ),
            []
        );

另一个解决方案可能是使用Set来收集唯一的类选择器。

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)
    ];

答案 1 :(得分:1)

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

  1. 使用spread syntax

    htmlCollection转换为数组
    [...htmlCollection] // -> [htmlElement, htmlElement, etc...]
    
  2. Array#map每个元素到一个类数组(再次传播):

    [htmlElement, htmlElement, etc...].map(({ classList }) => [...classList])) // -> [[class, class, etc...], [class, class, etc...], etc...]
    
  3. 通过传播到Array#concat

    来展平子阵列
    [].concat(...[[class, class, etc...], [class, class, etc...]], etc...) // -> [class, class, class, etc...]
    
  4. 将数组转换为Set,然后传播回数组,以获取唯一的类名:

    [...new Set([class, class, class, etc...])] // -> [class1, class2, class3, etc...]
    
  5. 
    
    const elements = document.getElementsByTagName('*');
    
    const classesUsedInHtml = (htmlCollection) =>
        [...new Set( // get unique items and convert to array
          [].concat(... // concat all classes' arrays
            [...htmlCollection] // convert the collection to an array
            .map(({ classList }) => [...classList])) // get arrays of classes
          )
        ];
        
    const result = classesUsedInHtml(elements);
    
    console.log(result);
    
    <div class="a c">
      <span class="a b"></span>
      <span class="b c"></span>
    </div>
    &#13;
    &#13;
    &#13;