Chessalyzer

Filtering games

Analyze only the games that match a JavaScript predicate.

Often you don't want every game — only the ones worth your question. Pass a filter: a plain function that receives each parsed game and returns true for the games to keep. Everything else is skipped before it costs you anything.

Only one game in our example file has a white player rated above 2000 (alice, 2114), so this analysis processes exactly one game:

import { analyzePGN } from 'chessalyzer';
import { tileTracker } from 'chessalyzer/trackers';

const tiles = tileTracker();

const result = await analyzePGN('games.pgn', {
    trackers: [tiles],
    filter: (game) => Number(game.headers?.WhiteElo) > 2000,
});

console.log(result.gameCount); // 1
console.log(result.moveCount); // 7

The filter receives the same ParsedGame shape you know from the parser — headers, result, moves. Header values are always strings, hence the Number(...). Headers are parsed automatically when a filter is present, so game.headers is always populated; leave headers: false alone unless your filter truly never looks at them.

Header keys are possibly undefined

Note how we needed to use the optional chaining operator (?.) when accessing the WhiteElo property. Since the exported header information can be different for each PGN database, Chessalyzer can't ensure that a certain header key is present in the data.

Filters run single-threaded

A filter is a JavaScript closure, and closures can't be shipped to worker threads — so when a filter is present, analyzePGN automatically analyzes on the main thread. Just don't combine a filter with explicit workers options; that's rejected as an error. On large files this costs real throughput, and Multithreading explains the trade-off and your options.

Handy patterns

Combine with maxGames to cap how many matching games are processed — a quick way to sample:

await analyzePGN('huge-database.pgn', {
    trackers: [tiles],
    filter: (game) => game.headers?.Result === '1/2-1/2',
    maxGames: 1000, // the first 1000 draws
});

And if you want to compare two filtered groups side by side — say, high-rated vs. low-rated players — you don't need two passes over the file: Comparing groups of games does both in one go.

On this page