2017-09-23 21 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

任何人都可以解釋除了提供的意見之外,整個解決方案中發生了什麼? –

相關問題