Skip to content

Instantly share code, notes, and snippets.

@vis97c
Last active January 5, 2024 16:09
Show Gist options
  • Save vis97c/9d28da6206b0a936e2f3aeaf012beba9 to your computer and use it in GitHub Desktop.
Save vis97c/9d28da6206b0a936e2f3aeaf012beba9 to your computer and use it in GitHub Desktop.
Separate country data from dr5hn/countries-states-cities-database into smaller files
const fs = require("fs/promises");
const path = require("path");
const _ = require("lodash");
/**
* Write file
* @param {string} filePath
* @param {*} data
*/
async function writeFile(filePath, data) {
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
}
/**
* Make dir
*
* @param {string[]} dirPaths
* @param {function(number)} callback
*/
async function makeDir(dirPaths, callback) {
if (await Promise.all(dirPaths.map((dirPath) => fs.mkdir(dirPath, { recursive: true })))) {
return callback(Date.now());
}
throw new Error("Couldn't create directories");
}
/**
* Handle duplicates
*
* @param {any[]} elementsOrigin
* @param {function(any)} elementCallback
*/
async function dedupe(elementsOrigin, elementCallback) {
const elements = elementsOrigin.reduce((acc, element, index) => {
const newElement = { ...element, index };
if (acc[element.name]) acc[element.name].push(newElement);
else acc[element.name] = [newElement];
return acc;
}, {});
for (let [elementName, elementArr] of Object.entries(elements)) {
// multiple with the same name
if (elementArr.length > 1) {
for (let i = 0; i < elementArr.length; i++) {
const element = elementArr[i];
// add number to element name
element.name = `${elementName} 0${i}`;
// update state
elementsOrigin[element.index] = element;
await elementCallback(_.omit(element, "index"), element.index);
}
continue;
}
// unique name element
await elementCallback(_.omit(elementArr[0], "index"), elementArr[0].index);
}
return elementsOrigin;
}
/**
* Main
*
* Data source:
* @see https://github.com/dr5hn/countries-states-cities-database/blob/master/countries+states+cities.json
*/
(async function () {
const start = Date.now();
let processed = 0;
let proccessTime = 0;
const file = "countries+states+cities.json";
const dataPath = path.join(__dirname, "data");
const countries = JSON.parse(
await fs.readFile(path.join(dataPath, file), {
encoding: "utf8",
})
);
const countriesPath = path.join(dataPath, "countries");
for (const country of countries) {
const countryPath = path.join(countriesPath, _.kebabCase(country.name));
const statesPath = path.join(countryPath, "states");
console.log(
`Creating files for ${country.name}, which has ${country.states.length} states`
);
makeDir([countryPath, statesPath], async (countryStart) => {
const states = await dedupe(country.states, async (state) => {
const statePath = path.join(statesPath, _.kebabCase(state.name));
const citiesPath = path.join(statePath, "cities");
console.log(
` Creating files for ${state.name} - ${country.name}, which has ${state.cities.length} cities`
);
await makeDir([statePath, citiesPath], async (stateStart) => {
const cities = await dedupe(state.cities, async (city) => {
// write city
await writeFile(
path.join(citiesPath, `${_.kebabCase(city.name)}.json`),
Object.assign({}, city, {
state: _.omit(state, "cities"),
country: _.omit(country, "states"),
})
);
});
// write state
await writeFile(
path.join(statePath, "/index.json"),
Object.assign({}, state, { country: _.omit(country, "states") })
);
// write state cities index
await writeFile(
path.join(citiesPath, "index.json"),
cities.map(({ id, name }) => ({ id, name }))
);
console.log(
"\x1b[32m%s\x1b[0m",
` Done with ${state.name} - ${country.name} in ${
Date.now() - stateStart
}ms`
);
});
});
// write country
await writeFile(path.join(countryPath, "/index.json"), country);
// write country states index
await writeFile(
path.join(statesPath, "index.json"),
states.map(({ id, name, state_code }) => ({ id, name, state_code }))
);
const countryProcessTime = Date.now() - countryStart;
processed += 1;
proccessTime += countryProcessTime;
console.log(
"\x1b[33m%s\x1b[0m",
`Done with ${country.name} in ${countryProcessTime}ms`
);
// last country
if (processed === countries.length) {
// write countries index
await writeFile(
path.join(countriesPath, "index.json"),
countries.map(({ id, name, iso2, phone_code, translations }) => ({
id,
name,
iso2,
phone_code,
translations,
}))
);
// time output
console.log(
"\x1b[34m%s\x1b[0m",
`Processed in ${Date.now() - start}ms, with a machine time of ${proccessTime}ms`
);
}
});
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment