From a7c83b9e89107a25441cb048ca6b5853a44c8a1d Mon Sep 17 00:00:00 2001 From: Marcel Gerber Date: Thu, 11 Apr 2024 17:28:58 +0200 Subject: [PATCH 1/3] enhance(regions): add ability to get region by name or variant name --- packages/@ourworldindata/utils/src/index.ts | 1 + packages/@ourworldindata/utils/src/regions.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts index e09e67f63c7..f3d30808e5e 100644 --- a/packages/@ourworldindata/utils/src/index.ts +++ b/packages/@ourworldindata/utils/src/index.ts @@ -240,6 +240,7 @@ export { countries, type Country, getCountryBySlug, + getRegionByNameOrVariantName, isCountryName, continents, type Continent, diff --git a/packages/@ourworldindata/utils/src/regions.ts b/packages/@ourworldindata/utils/src/regions.ts index cbb94f9f62d..b80deb1ff65 100644 --- a/packages/@ourworldindata/utils/src/regions.ts +++ b/packages/@ourworldindata/utils/src/regions.ts @@ -67,6 +67,18 @@ const countriesBySlug: Record = Object.fromEntries( countries.map((country) => [country.slug, country]) ) +const regionsByNameOrVariantNameLowercase: Map = new Map( + regions.flatMap((region) => { + const names = [region.name.toLowerCase()] + if ("variantNames" in region && region.variantNames) { + names.push( + ...region.variantNames.map((variant) => variant.toLowerCase()) + ) + } + return names.map((name) => [name, region]) + }) +) + const currentAndHistoricalCountryNames = regions .filter(({ regionType }) => regionType === "country") .map(({ name }) => name.toLowerCase()) @@ -76,3 +88,8 @@ export const isCountryName = (name: string): boolean => export const getCountryBySlug = (slug: string): Country | undefined => countriesBySlug[slug] + +export const getRegionByNameOrVariantName = ( + nameOrVariantName: string +): Region | undefined => + regionsByNameOrVariantNameLowercase.get(nameOrVariantName.toLowerCase()) From 7ced9932f43ef24e90a8cceafdfd0946c3c4d1fa Mon Sep 17 00:00:00 2001 From: Marcel Gerber Date: Thu, 11 Apr 2024 17:29:12 +0200 Subject: [PATCH 2/3] enhance(search): util to match region names in search query --- packages/@ourworldindata/utils/src/index.ts | 1 + site/search/SearchUtils.tsx | 24 +++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 site/search/SearchUtils.tsx diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts index f3d30808e5e..fccb5e5b961 100644 --- a/packages/@ourworldindata/utils/src/index.ts +++ b/packages/@ourworldindata/utils/src/index.ts @@ -35,6 +35,7 @@ export { isTouchDevice, type Json, csvEscape, + escapeRegExp, urlToSlug, trimObject, fetchText, diff --git a/site/search/SearchUtils.tsx b/site/search/SearchUtils.tsx new file mode 100644 index 00000000000..2587d56e6f7 --- /dev/null +++ b/site/search/SearchUtils.tsx @@ -0,0 +1,24 @@ +import { + Region, + getRegionByNameOrVariantName, + regions, + escapeRegExp, +} from "@ourworldindata/utils" + +const allCountryNamesAndVariants = regions.flatMap((c) => [ + c.name, + ...(("variantNames" in c && c.variantNames) || []), +]) + +// A RegExp that matches any country, region and variant name. Case-independent. +const regionNameRegex = new RegExp( + `\\b(${allCountryNamesAndVariants.map(escapeRegExp).join("|")})\\b`, + "gi" +) + +export const extractRegionNamesFromSearchQuery = (query: string) => { + const matches = query.matchAll(regionNameRegex) + const regionNames = Array.from(matches, (match) => match[0]) + if (regionNames.length === 0) return null + return regionNames.map(getRegionByNameOrVariantName) as Region[] +} From 41bbdcc7382eb237eff27147b1fcce00add1f638 Mon Sep 17 00:00:00 2001 From: Marcel Gerber Date: Thu, 11 Apr 2024 18:21:39 +0200 Subject: [PATCH 3/3] test(regions): add test case --- .../@ourworldindata/utils/src/regions.test.ts | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/@ourworldindata/utils/src/regions.test.ts b/packages/@ourworldindata/utils/src/regions.test.ts index 18afc40edeb..0c1d78a217c 100755 --- a/packages/@ourworldindata/utils/src/regions.test.ts +++ b/packages/@ourworldindata/utils/src/regions.test.ts @@ -1,6 +1,10 @@ #! /usr/bin/env jest -import { isCountryName, getCountryBySlug } from "./regions.js" +import { + isCountryName, + getCountryBySlug, + getRegionByNameOrVariantName, +} from "./regions.js" it("isCountryName", () => { expect(isCountryName("United States")).toEqual(true) @@ -15,3 +19,30 @@ it("getCountryBySlug", () => { }) expect(getCountryBySlug("not-a-country")).toEqual(undefined) }) + +it("getRegionByNameOrVariantName", () => { + expect(getRegionByNameOrVariantName("United States")).toMatchObject({ + name: "United States", + slug: "united-states", + code: "USA", + }) + + expect(getRegionByNameOrVariantName("USA")).toMatchObject({ + name: "United States", + slug: "united-states", + code: "USA", + }) + + // Test case-insensitivity + expect(getRegionByNameOrVariantName("UNITED KINGDOM")).toMatchObject({ + name: "United Kingdom", + slug: "united-kingdom", + code: "GBR", + }) + + expect(getRegionByNameOrVariantName("uae")).toMatchObject({ + name: "United Arab Emirates", + slug: "united-arab-emirates", + code: "ARE", + }) +})