How to Count in JavaScript
JavaScript can count several different things, and the right pattern depends on whether you need a collection size, matching items, frequencies, text units, DOM matches, diagnostic calls, or application state.
To count in JavaScript, use
.lengthfor collection size,filter(),reduce(), or a loop for matches,Mapfor frequencies,Intl.Segmenterfor user-perceived characters,querySelectorAll().lengthfor DOM elements, and a variable for application state.
| What you need | Direct pattern | Result |
|---|---|---|
| Array size | items.length | Number of slots |
| Matching array values | items.filter(test).length | One match count |
| Every distinct value | A Map frequency table | Value-to-count pairs |
| Text units | .length, iteration, or Intl.Segmenter | Code units, code points, or graphemes |
| Matching DOM elements | document.querySelectorAll(selector).length | Number of current matches |
| Calls or page state | console.count() or a variable | Diagnostic output or stored state |
What Does “Count” Mean in JavaScript?
JavaScript has no general-purpose count() function. First decide what the number represents.
A collection size is the number of slots or entries already in a collection. Arrays expose that number through length.
A conditional count asks how many values pass a test, such as how many orders have a status of "ready". filter(...).length, reduce(), and for...of all fit that job.
A frequency table counts every distinct value. Instead of returning one number, it associates each value with its occurrence count. A Map handles that shape directly.
A text or DOM count depends on the unit. Text can be counted as UTF-16 code units, Unicode code points, user-perceived characters, or words. DOM queries return a list whose length reports how many elements matched when the query ran.
The fifth category is execution or UI state. console.count() reports how often a labeled line runs during debugging. A visible page counter needs a numeric variable that your event handlers update and render.
Those categories overlap in syntax, but not in meaning. Reading orders.length answers how many slots the array has. It does not answer how many orders are ready.
Count Items or Matching Values in an Array
For an ordinary dense array, length gives the collection size:
const tasks = ["draft", "review", "publish"];
console.log(tasks.length);
3
The result is 3 because the array has three slots. Arrays covers how indexes and length move when you add or remove items.
Say you need the number of "ready" values instead. filter() keeps the matching elements in a new array, and length counts that result:
const statuses = ["ready", "waiting", "ready", "failed"];
const readyCount = statuses.filter(status => status === "ready").length;
console.log(readyCount);
2
The callback uses strict equality, ===, so "ready" only matches the same primitive string value. Comparisons explains why strict equality is the safer default when values may have different types.
filter(...).length reads close to the question, but filter() creates a shallow result array. When you only need the number, reduce() can carry that number through the original array:
const statuses = ["ready", "waiting", "ready", "failed"];
const readyCount = statuses.reduce(
(count, status) => count + (status === "ready" ? 1 : 0),
0
);
console.log(readyCount);
2
The initial 0 matters. It makes the accumulator a count from the first callback onward, and it lets an empty array return 0. Calling reduce() on an empty array without an initial value throws a TypeError.
A loop makes the update explicit and leaves room for extra conditions or an early exit:
const orders = [
{ id: 101, status: "ready" },
{ id: 102, status: "waiting" },
{ id: 103, status: "ready" },
];
let readyCount = 0;
for (const order of orders) {
if (order.status === "ready") {
readyCount += 1;
}
}
console.log(readyCount);
2
This time the comparison reads an object property. Two orders pass, so the counter reaches 2.
The three matching patterns have different shapes:
filter(...).lengthstates the condition clearly and creates a result array.reduce()returns the count directly and needs an explicit initial value.for...ofexposes each update and can perform other work in the same pass.
None is universally fastest. If the difference matters for a real workload, measure that workload.
Sparse arrays need a separate rule. An array’s length counts slots, including empty ones, while filter() and reduce() skip empty slots:
const readings = [];
readings.length = 4;
readings[2] = 18;
const assignedWithFilter = readings.filter(() => true).length;
const assignedWithReduce = readings.reduce(count => count + 1, 0);
console.log(readings.length);
console.log(assignedWithFilter);
console.log(assignedWithReduce);
4
1
1
The array has four slots but only one assigned element. That distinction disappears in most dense arrays, which is why sparse arrays deserve their own check.
One equality edge remains. value === NaN is always false, and two separate object literals are different references even when their properties match. For those cases, count by a property, use Number.isNaN(), or build the frequency table around the identity you actually mean.
Build a Frequency Table for Every Value
Counting one requested value returns one number. A frequency table, also called a histogram, returns one count for every distinct value.
A Map fits because its keys can be arbitrary JavaScript values. Start with no stored count, substitute 0, add one, and write the new count back:
const statuses = ["ready", "waiting", "ready", "failed", "waiting", "ready"];
const frequencies = statuses.reduce((counts, status) => {
counts.set(status, (counts.get(status) ?? 0) + 1);
return counts;
}, new Map());
console.log(frequencies.get("ready"));
console.log(frequencies.get("waiting"));
console.log(frequencies.get("failed"));
3
2
1
The Map begins empty. The first "ready" reads as missing, becomes 0, and is stored as 1; later occurrences update the same entry.
Map compares keys with SameValueZero. That comparison treats NaN as equal to NaN, treats +0 and -0 as the same key, and keeps separate objects distinct unless they are the same reference:
const sensor = { id: 7 };
const values = [NaN, NaN, -0, +0, sensor, sensor, { id: 7 }];
const frequencies = new Map();
for (const value of values) {
frequencies.set(value, (frequencies.get(value) ?? 0) + 1);
}
console.log(frequencies.get(NaN));
console.log(frequencies.get(0));
console.log(frequencies.get(sensor));
console.log(frequencies.get({ id: 7 }));
2
2
2
undefined
The two NaN values share a count, as do -0 and +0. The repeated sensor reference also shares a count. The fresh { id: 7 } literal is another object, so it has no entry.
That is different from the strict equality occurrence pattern. status === "ready" works for a requested primitive value, but reading === NaN does not. Data types provides the larger map of primitive values and objects behind those comparisons.
When every key is already a string, a null-prototype object is a smaller alternative:
const labels = ["draft", "sent", "draft"];
const frequencies = Object.create(null);
for (const label of labels) {
frequencies[label] = (frequencies[label] ?? 0) + 1;
}
console.log(frequencies.draft);
console.log(frequencies.sent);
console.log(Object.getPrototypeOf(frequencies));
2
1
null
Object.create(null) removes inherited properties, so names such as "toString" do not arrive from Object.prototype. Use this form for string-key dictionaries. Use Map when keys include numbers, NaN, objects, or other value types.
Count Characters, Words, and DOM Elements
A JavaScript string has more than one useful length. The right number depends on what a “character” means for the task.
string.length counts UTF-16 code units. String iteration counts Unicode code points. Neither is guaranteed to match the characters a person sees, because one grapheme can contain several code points.
The difference appears in a short string containing an emoji and a combining mark:
const text = "A😀e\u0301";
const graphemes = new Intl.Segmenter(undefined, {
granularity: "grapheme",
});
console.log(text.length);
console.log(Array.from(text).length);
console.log(Array.from(graphemes.segment(text)).length);
5
4
3
The text displays three user-perceived characters: A, 😀, and é. JavaScript stores them as five UTF-16 code units, string iteration produces four code points, and Intl.Segmenter groups them into three graphemes.
Use the unit that matches the rule:
- Use
text.lengthwhen an API limit is defined in UTF-16 code units. - Use
Array.from(text).lengthwhen you need Unicode code points. - Use
Intl.Segmenterwithgranularity: "grapheme"for user-perceived characters. - Use
Intl.Segmenterwithgranularity: "word"when you need language-aware word segments.
Here is a word count that ignores spaces and punctuation segments:
const message = "Ship two small fixes.";
const words = new Intl.Segmenter("en", {
granularity: "word",
});
const wordCount = Array.from(words.segment(message))
.filter(segment => segment.isWordLike)
.length;
console.log(wordCount);
4
The segmenter finds four word-like segments. A plain split(" ") can count this sentence too, but repeated whitespace and punctuation rules soon make that shortcut describe the separator rather than the words.
DOM counting uses another kind of collection. querySelectorAll() returns a static NodeList, and its length reports how many elements matched at the time of the query:
const cardsWithoutHiddenAttribute = document.querySelectorAll(
'.card:not([hidden])'
).length;
console.log(cardsWithoutHiddenAttribute);
This browser fragment has no fixed output because the number depends on the current document. If the DOM changes later, run the query again to obtain a new count. Styles and classes covers the class and attribute changes that often control those matches.
Count How Often Code Runs with console.count()
console.count() is a diagnostic counter. Each call prints the label and the number of times that label has reached console.count().
Omitting the argument uses the label "default". Named labels keep separate counters:
console.count();
console.count();
console.count("save");
console.count("save");
console.count("render");
default: 1
default: 2
save: 1
save: 2
render: 1
The labels separate unrelated paths. Put console.count("render") in a render function, for example, and the console shows how many times that call runs while you reproduce a problem.
console.countReset() resets one counter to zero. With no argument, it resets the default counter:
console.count("request");
console.count("request");
console.countReset("request");
console.count("request");
console.count();
console.countReset();
console.count();
const result = console.count("return value");
console.log(result);
request: 1
request: 2
request: 1
default: 1
default: 1
return value: 1
undefined
After each reset, the next call prints 1. The final two lines show another rule: console.count() prints diagnostic information and returns undefined.
Use a variable when program behavior depends on the number. Use console.count() when you need to observe execution while debugging. The developer console covers the environment where those labels appear.
Build a Counter for the Page
A page counter needs explicit numeric state. The buttons change that state, and one render function copies it into the document.
This is the complete markup for an increment, decrement, and reset counter:
<p>
Count:
<output id="count" aria-live="polite">0</output>
</p>
<button id="decrement" type="button">Decrease</button>
<button id="increment" type="button">Increase</button>
<button id="reset" type="button">Reset</button>
<script src="counter.js"></script>
The output element displays the current value. aria-live="polite" lets assistive technology announce changes without interrupting the current speech.
Here is the complete counter.js implementation:
const countOutput = document.querySelector("#count");
const decrementButton = document.querySelector("#decrement");
const incrementButton = document.querySelector("#increment");
const resetButton = document.querySelector("#reset");
let count = 0;
function renderCount() {
countOutput.value = count;
}
decrementButton.addEventListener("click", () => {
count -= 1;
renderCount();
});
incrementButton.addEventListener("click", () => {
count += 1;
renderCount();
});
resetButton.addEventListener("click", () => {
count = 0;
renderCount();
});
renderCount();
count is the source of truth. Each listener changes the number first, then renderCount() updates the page. The DOM displays state; it does not own it.
This is the same state flow used in the larger JavaScript Counter: Build One Step by Step. The arithmetic and event handling underneath also appear throughout JavaScript Fundamentals when you want the full course offline.
Which Counting Pattern Should You Use?
Choose from the result you need, not from the method name you remember.
Use array.length for the number of array slots. Use filter(...).length when readability matters and the filtered array allocation is acceptable. Use reduce() or a loop when you want the count without that result array.
Use a Map when the output needs one count per distinct value. Use text iteration for code points, and Intl.Segmenter when the unit is a grapheme or word. Use querySelectorAll(selector).length for the DOM elements matching a selector when the query runs.
Five mistakes account for most wrong counts:
- Reading
lengthwhen the question asks for matching values. - Calling
reduce()on an empty array without an initial count. - Using loose equality and allowing type conversion into the match.
- Calling UTF-16 code units user-perceived characters.
- Treating
console.count()as stored UI state.
The method can be correct while the unit is wrong. Name the thing being counted first.
Frequently asked questions
How do you count items in JavaScript?
array.length when you need the number of array slots. To count only matching values, use filter(...).length, reduce(), or a loop.How do you count occurrences of a value in a JavaScript array?
array.filter(item => item === value).length for a clear occurrence count. A loop or reduce() avoids creating the filtered result array.How do you count each value in a JavaScript array?
Map. Read the current count with get(), add one, and store it again with set() for every array element.How do you count characters correctly in JavaScript?
string.length counts UTF-16 code units, and Array.from(string).length counts Unicode code points. Use Intl.Segmenter with grapheme granularity when the count should follow user-perceived characters.What does console.count() do in JavaScript?
console.count() logs how many times execution has reached that call for a given label. It uses default when no label is supplied, and console.countReset() resets the selected counter.