-
-
Save dr5hn/ea6181019b178fc6556e6d91c55cfb4b to your computer and use it in GitHub Desktop.
Separate country data from dr5hn/countries-states-cities-database into smaller files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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