Skip to content

Commit

Permalink
refactor: introduce static initializers for FuzzySearch
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Mar 3, 2025
1 parent f7ba408 commit 60895b1
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,9 @@ export class EntityPicker extends React.Component<{
}

@computed private get fuzzy(): FuzzySearch<EntityOptionWithMetricValue> {
return new FuzzySearch(
return FuzzySearch.withKey(
this.entitiesWithMetricValue,
OwidTableSlugs.entityName
(entity) => entity.entityName
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,10 @@ export class EntitySelector extends React.Component<{
}

@computed get fuzzy(): FuzzySearch<SearchableEntity> {
return new FuzzySearch(this.sortedAvailableEntities, "name")
return FuzzySearch.withKey(
this.sortedAvailableEntities,
(entity) => entity.name
)
}

@computed get searchResults(): SearchableEntity[] | undefined {
Expand Down
41 changes: 38 additions & 3 deletions packages/@ourworldindata/utils/src/FuzzySearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,47 @@ export class FuzzySearch<T> {
datamap: Record<string, T[]>
opts: Fuzzysort.Options | undefined

constructor(data: T[], key: keyof T, opts?: Fuzzysort.Options) {
this.datamap = groupBy(data, key)
this.strings = data.map((d) => fuzzysort.prepare(d[key] as string))
private constructor(
datamap: Record<string, T[]>,
opts?: Fuzzysort.Options
) {
const rawStrings = Object.keys(datamap)
this.strings = rawStrings.map((s) => fuzzysort.prepare(s))
this.datamap = datamap
this.opts = opts
}

static withKey<T>(
data: T[],
key: (obj: T) => string,
opts?: Fuzzysort.Options
): FuzzySearch<T> {
const datamap = groupBy(data, key)
return new FuzzySearch(datamap, opts)
}

// Allows for multiple keys per object, e.g. aliases:
// [
// { name: "Netherlands", "keys": ["Netherlands", "Nederland"] },
// { name: "Spain", "keys": ["Spain", "España"] },
// ]
// Note that the calling site will need to take care of uniqueness of results,
// if that's desired, e.g. using uniqBy(results, "name")
static withKeyArray<T>(
data: T[],
keys: (obj: T) => string[],
opts?: Fuzzysort.Options
): FuzzySearch<T> {
const datamap: Record<string, T[]> = {}
data.forEach((d) => {
keys(d).forEach((key) => {
if (!datamap[key]) datamap[key] = [d]
else datamap[key].push(d)
})
})
return new FuzzySearch(datamap, opts)
}

search(input: string): T[] {
return fuzzysort
.go(input, this.strings, this.opts)
Expand Down
4 changes: 3 additions & 1 deletion site/runCountryProfilePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class ChartFilter {
@observable query: string = ""

@computed private get fuzzy(): FuzzySearch<ChartItem> {
return new FuzzySearch(this.chartItems, "title", { threshold: -150 })
return FuzzySearch.withKey(this.chartItems, (chart) => chart.title, {
threshold: -150,
})
}

@computed get searchResults() {
Expand Down

0 comments on commit 60895b1

Please sign in to comment.