2017-09-23 51 views
2

我写了一个JavaScript函数,它成功返回了HTML文档中使用的所有唯一类的数组。将对象数组减少为在所有对象共享的属性中找到的唯一字符串(javascript)

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未定义? 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) 
    ); 
    }, []); 
}; 

编辑:取出从有问题的功能uneccessary map()由于尼娜肖尔茨评论

+1

为什么地图,如果你做不接受返回值(在第一个代码中)?第二个不会用'forEach'返回你想要的。它只返回undefined。 –

+0

赶上@NinaScholz。我会更新上面的问题函数。但那不会解决潜在的问题。 –

回答

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

'DOMTokenList'不是一个数组,但它的原型中有'forEach()'方法。所以如果你添加'Array.from(domTokenList).reduce ...',你的代码就可以工作。在数组上使用另一个'reduce()'是个好主意。但是,你有没有洞察力如何使用'forEach()'工作? –

+1

正如我在评论中写的,'forEach'不会返回任何内容,以便与之协作。在这种情况下,你需要一个外部变量,你想消除这个变量。所以要么采取“地图”,要么减少获得新的价值。 –

+0

谢谢,在我发布之前没有看到您更新的评论。 –

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...] 
    

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>

+0

哇!我知道我不应该使用“谢谢”的评论,但我不知道“Set”。使用扩展运算符真的很优雅,就像我在寻找的那种方法。 –

+1

@BrianZ - 欢迎:) –

+0

任何人都可以解释除了提供的意见之外,整个解决方案中发生了什么? –

相关问题