Skip to content

Commit

Permalink
feat: expose array-like methods for searching data (#4)
Browse files Browse the repository at this point in the history
* feat: expose array-like methods for searching data

Instead of limiting an end-user to a string search, enable `predicate` functions to `find` and `filter` values.

BREAKING CHANGE: exposed api changed from string search to predicate methods

* docs: update readme

* feat: data no longer lowercased or characters removed

* fix: correct function declaration name

Co-authored-by: forstermatth <matt@autovance.com>

Co-authored-by: forstermatth <matt@autovance.com>
  • Loading branch information
Tyler Stewart and forstermatth authored Aug 4, 2020
1 parent dd05a6f commit 69e0603
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 55 deletions.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"name": "canadian-city-timezones",
"version": "1.0.0",
"version": "2.0.0",
"description": "Searchable timezones for all Canadian cities, towns, townships, villages, hamlets, and municipalities.",
"main": "src/index.js",
"types": "src/index.d.ts",
"files": [
"src/index.js",
"src/index.d.ts",
"src/util.js",
"src/data.csv"
],
"scripts": {
Expand All @@ -20,6 +19,10 @@
"timezones"
],
"author": "trs",
"repository": {
"type": "git",
"url": "https://github.com/autovance/canadian-city-timezones"
},
"license": "MIT",
"devDependencies": {
"csv-parser": "^2.3.3",
Expand Down
18 changes: 12 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
`npm install canadian-city-timezones`

```ts
import {find, findAll} from 'canadian-city-timezones';
import {find} from 'canadian-city-timezones';

const result = await find('Lethbridge Alberta');
const result = await find((city, province) => city === 'Lethbridge' && province === 'Alberta');
result.city // Lethbridge
result.province // Alberta
result.timezone // America/Edmonton
Expand All @@ -24,16 +24,22 @@ result.timezone // America/Edmonton
### Methods

```ts
find(query: string): Promise<TimezoneResult>
find(predicate: (value: TimezoneResult) => boolean): Promise<TimezoneResult | null>
```

Returns the first matching result for the given string.
Returns the first matching result for the given predicate.

```ts
findAll(query: string): Promise<TimezoneResult[]>
filter(predicate: (value: TimezoneResult) => boolean): AsyncGenerator<TimezoneResult>
```

Returns all matching results for the given string.
Yields all matching results for the given predicate.

```ts
values(): AsyncGenerator<TimezoneResult>
```

Yields all values.

### Interfaces

Expand Down
7 changes: 3 additions & 4 deletions src/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const fetch = require('node-fetch');
const csv = require('csv-parser');
const geoTz = require('geo-tz');
const removeAccents = require('remove-accents');
const {removeSpecialCharacters} = require('./util');

const mkdirAsync = promisify(mkdir);
const pipelineAsync = promisify(pipeline);
Expand All @@ -31,7 +30,7 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function * getCityData() {
const req = await fetch(CAN_CITY_LIST);
// Download file first as the csv parser would prematurely end being piped it directly
// Download file first as the csv parser would prematurely end being piped in directly
await pipelineAsync(
req.body,
createWriteStream('gc.csv')
Expand Down Expand Up @@ -90,8 +89,8 @@ async function * generateData() {
}

yield [
removeSpecialCharacters(cityData.name),
removeSpecialCharacters(cityData.province),
cityData.name,
cityData.province,
timezone
].join(',');

Expand Down
8 changes: 6 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ declare interface TimezoneResult {
timezone: string;
}

declare function find(query: string): Promise<TimezoneResult>;
declare type Predicate = (data: TimezoneResult) => boolean;

declare function findAll(query: string): Promise<TimezoneResult[]>;
declare function values(): AsyncGenerator<TimezoneResult>;

declare function find(predicate: Predicate): Promise<TimezoneResult | null>;

declare function filter(predicate: Predicate): AsyncGenerator<TimezoneResult>;
37 changes: 15 additions & 22 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const path = require('path');
const {createReadStream} = require('fs');
const {createInterface} = require('readline');
const {removeSpecialCharacters} = require('./util');

const CITY_MAP_DATA = path.join(__dirname, 'data.csv');

Expand All @@ -28,43 +27,37 @@ async function * getDataIterator() {
}
}

const isPartialMatchFactory = (query) => {
const searchItems = query.split(' ').map((item) => removeSpecialCharacters(item));

return (data) => {
const values = [
data.city,
data.province
];

return searchItems.every((item) => values.join().includes(item));
}
async function* values() {
yield* getDataIterator();
}

async function find(query) {
const isPartialMatch = isPartialMatchFactory(query);
async function find(predicate) {
if (typeof predicate !== 'function') {
throw new TypeError(`${String(predicate)} is not a function`);
}

for await (const data of getDataIterator()) {
if (isPartialMatch(data)) {
if (predicate(data)) {
return data;
}
}
return null;
}

async function findAll(query) {
const isPartialMatch = isPartialMatchFactory(query);
async function* filter(predicate) {
if (typeof predicate !== 'function') {
throw new TypeError(`${String(predicate)} is not a function`);
}

const results = [];
for await (const data of getDataIterator()) {
if (isPartialMatch(data)) {
results.push(data);
if (predicate(data)) {
yield data;
}
}
return results;
}

module.exports = {
values,
find,
findAll
filter
};
9 changes: 0 additions & 9 deletions src/util.js

This file was deleted.

10 changes: 0 additions & 10 deletions test/util.spec.js

This file was deleted.

0 comments on commit 69e0603

Please sign in to comment.