如果您想計算數組中有多少特定品種的項目,您可以過濾該數組並檢查結果的長度。
const letters = ['a','b','b','c','c','c']; const numberOfC = letters.filter(letter => letter === 'c').length; console.log(numberOfC); // 3
時間複雜度為 O(n),這是此類問題的理論理想值。它確實使用一些額外的記憶體來保存第二個數組,但對於僅計算一種項目,這是一種常見的方法,並且在大多數情況下足夠高效。但是,如果您想要計算數組中每種項目的數量,那麼這不是一種有效的方法。
如果我們不僅想要計算「c」的數量,還想要計算數組中每個字母的數量,那麼這種技術的簡單應用將是這樣的:
const letters = ['a','b','b','c','c','c']; letters.forEach((letter, _, arr) => { const count = arr.filter(otherLetter => otherLetter === letter).length; console.log(letter, count); });
這會產生以下輸出:
a 1 b 2 b 2 c 3 c 3 c 3
計數是正確的,但是存在冗餘,可以透過先使用 Set 來修復。
const letters = ['a','b','b','c','c','c']; const uniqueLetters = new Set(letters); for(const letter of uniqueLetters){ const count = letters.filter(otherLetter => otherLetter === letter).length; console.log(letter, count); };
結果輸出沒有重複:
a 1 b 2 c 3
但是,它的時間複雜度確實為 O(n^2),因為每個過濾器都會迭代整個數組,並且它必須對每個唯一的字母執行此操作。
可以以 O(n) 的時間複雜度執行此計數。一般原則是只迭代數組一次。對於遇到的每個項目,請檢查您是否已經有該項目的計數。如果您這樣做,請將其增加 1。如果您不這樣做,請為該項目新增一個計數,並將值設為 1。
Javascript 有兩種不同的機制適合儲存計數。我們可以使用物件或地圖。請注意,這是對 Map 資料結構的引用,而不是數組的 map() 方法。 使用 Map 效率更高,但使用物件來實現此目的仍然很常見,因此我們將了解如何使用兩者進行計數。
const letters = ['a','b','b','c','c','c']; const counts = new Map(); // Creates an empty Map letters.forEach(letter => { if(counts.has(letter)) {//Determine if the letter already has a count counts.set(letter, counts.get(letter) + 1);//Increment existing count } else counts.set(letter, 1);//Set new count to 1 }); console.log(counts); //Map(3) { 'a' => 1, 'b' => 2, 'c' => 3 }
has() 檢查該值是否已在 Map 中。 set() 在 Map 中儲存一個值,或是建立一個新值,或是覆寫現有值。 get() 取得目前值。 請注意,has()、set() 和 get() 的時間複雜度為 O(1),因為內部使用了雜湊。
如果我們想稍後提取特定字母的計數,我們可以這樣做:
console.log(counts.get('c'));//3
雖然使用物件進行計數速度較慢,但如果您沒有計算大量的項目,則效能差異可能不會明顯。物件也比地圖擁有更多的瀏覽器支持,儘管此時這種差異幾乎變得無關緊要。據 caniuse.com 稱,在撰寫本文時,96.28% 的瀏覽器都支援 Map。物件是 Javascript 語言的一個組成部分,應該有 100% 的瀏覽器支持,但 caniuse.com 沒有它的清單。 學習如何使用物件進行計數的主要原因是因為存在許多使用此技術的舊程式碼。一些從舊程式碼中學習的人也使用這種方法編寫新程式碼。
const letters = ['a','b','b','c','c','c']; const counts = {};//Create empty object for holding counts letters.forEach(letter => { if(letter in counts) {//Check if the count exists counts[letter]++;//Increment the count } else counts[letter] = 1;//Set new count to 1 }); console.log(counts);//{ a: 1, b: 2, c: 3 }
此程式碼遵循與使用 Map 的程式碼相同的結構。差異在於映射和物件的語法。 “in”關鍵字用於測試物件是否具有具有該名稱的鍵。括號語法用於取得和設定值。 請注意,測試成員資格、設定物件以及從物件取得值是 O(1) 時間複雜度操作,因為物件內部使用鍵的雜湊值。
如果您正在數的東西是物體,則需要格外小心。映射能夠將對象存儲為鍵,並且保留類型,但是如果您有一個完全不同的對象,並且具有完全相同的鍵和值,那麼這會在相等比較方面產生問題。
const m = new Map(); m.set({name: "John"}, 5); console.log(m.has({name: "John"}));//false
此程式碼輸出“false”,因為兩個物件不具有引用相等性。 這兩個物件的鍵和值相同,但它們不是完全相同的物件。
const m = new Map(); const obj = {name: "John"} m.set(obj, 5); console.log(m.has(obj));//true
此版本的程式碼輸出“true”,因為值集與正在測試成員資格的物件完全相同。
在大多數情況下嘗試使用 Map 來計算物件的結果是所有物件的計數最終都會為 1,因為每個 has() 都會傳回 false。
使用物件來計數物件有一個相關但略有不同的問題。物件鍵自動強制轉換為字串 (除非它們是符號)。如果您嘗試使用一個物件作為另一個物件的鍵,則該類型強制會建立一個等於「[object Object]」的鍵。
const obj = {name: "John"} const count = {}; count[obj] = 5; console.log(count);// { '[object Object]': 5 }
The result of trying to count objects using objects is you will end up with an object that has "[object Object]" as the key, and the value will be the total number of all the things that were counted. There won't be any individual counts, because every item is treated as "[object Object]".
It is not common that you would need to count objects. If the need arises, the solution depends on whether or not the objects have a unique property that is guaranteed to be unique. For example, objects that have an id number could be counted by counting the id number occurrences instead of counting occurrences of the whole object.
const employees = [ {id: 1, name: "John"}, {id: 1, name: "John"}, {id: 2, name: "Mary"}, {id: 2, name: "Mary"}, {id: 2, name: "Mary"}, ] const counts = new Map(); employees.forEach(employee => { if(counts.has(employee.id)) { counts.set(employee.id, counts.get(employee.id) + 1); } else counts.set(employee.id, 1); }); console.log(counts);//Map(2) { 1 => 2, 2 => 3 }
This only gives counts with ids and an extra lookup would be needed to associate the counts with names, but we can fix that with a bit more code:
const countsWithNames = []; for(const count of counts) { const employee = employees.find((employee) => employee.id === count[0]); countsWithNames.push({employee, count: count[1]}); } console.log(countsWithNames);
The resulting output is:
[ { employee: { id: 1, name: 'John' }, count: 2 }, { employee: { id: 2, name: 'Mary' }, count: 3 } ]
If there isn't anything like an id that guarantees uniqueness, the only remaining practical option would be to first convert the object to JSON.
const people = [ {name: "John Doe", age: 25}, {name: "John Doe", age: 25}, {name: "Mary Jane", age: 24}, {name: "Mary Jane", age: 24}, {name: "Mary Jane", age: 24} ] const counts = new Map(); people.forEach(person => { const personString = JSON.stringify(person); if(counts.has(personString)) { counts.set(personString, counts.get(personString) + 1); } else counts.set(personString, 1); }); console.log(counts);
The resulting output is:
Map(2) { '{"name":"John Doe","age":25}' => 2, '{"name":"Mary Jane","age":24}' => 3 }
Note that there are limitations to this approach. For example, objects with circular references cannot be stringified using JSON.stringify().
Both the techniques for counting with Maps and for counting with objects can be used to count anything, as long as you can find a way to iterate through the items. Some types of data can be converted to an array, for example, strings can be split, and keys of objects can be accessed as an array via Object.keys().
There are also some iterables that don't need to be converted into an array. For example, it's possible to iterate through the characters of a string by using an index. The same principle can be used in array-like structures like HTML Collections.
以上是用 Javascript 計算東西的詳細內容。更多資訊請關注PHP中文網其他相關文章!