search-gold 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +46 -22
  2. package/package.json +10 -10
  3. package/src/search.ts +23 -9
package/README.md CHANGED
@@ -2,20 +2,28 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Your web page displays a list of columnar information, based on the common data structure:
5
+ Your web page displays a list of columnar information, most often coming from one of the following two common data structures:
6
6
 
7
- `array of objects`
7
+ ```javascript
8
+ // an array of objects
9
+ Record<string, any>[]
10
+
11
+ // a Map in which the unique key's value is an object
12
+ Map<string, Record<string, any>>
13
+ ````
8
14
 
9
- For an improved UX, you want to add a search field so the user can filter the displayed records.
15
+ For an improved UX in the front end, you can add a search field so the user can filter the displayed records.
10
16
 
11
- The `search-gold` package is easy to set up and use!
17
+ The `search-gold` search engine is easy to set up and use!
12
18
 
13
19
  ## Installation
14
20
 
15
21
  ```bash
16
22
  $ npm i search-gold
17
23
  ```
24
+
18
25
  OR
26
+
19
27
  ```bash
20
28
  $ bun i search-gold
21
29
  ```
@@ -26,11 +34,16 @@ The following should provide enough to get local search up and running and keep
26
34
 
27
35
  Provide the following in your component, e.g., `my-data-list-component.ts`.
28
36
 
29
- ### 1. Import the two types and methods.
37
+ ### 1. Import applicable types and methods.
38
+
30
39
  ```javascript
31
40
  import {
41
+ // types
32
42
  ConfigParams,
33
43
  ItemRecord,
44
+ MapRecords,
45
+
46
+ // methods
34
47
  searchHandler,
35
48
  searchSetConfig,
36
49
  } from 'search-gold'
@@ -40,30 +53,32 @@ import {
40
53
 
41
54
  This wrapper method only needs to be called in the following two cases:
42
55
 
43
- * when component is initialized and when data is ready
44
- * when data changes (record has been created, edited, deleted)
56
+ - when component is initialized and when data is ready
57
+ - when data changes (record has been created, edited, deleted)
45
58
 
46
59
  You can name this wrapper method anything you want.
47
60
 
48
61
  ```javascript
49
62
  const initSearchConfig = () => {
50
63
  const config: ConfigParams = {
51
- // required: items must reference an array of
52
- // objects.
64
+ // Required: items must reference an array of
65
+ // objects or a Map<string, Record<string, any>>.
66
+ //
67
+ // Search results are returned in a separate array or Map.
53
68
  //
54
- // 'search-gold' package only reads this data.
55
- // Search results are returned in a separate array.
69
+ // 'search-gold' package only reads the original data,
70
+ // leaving it unchanged.
56
71
  items: MyData,
57
72
 
58
- // required: keys are the object properties you
73
+ // Required: keys are the object properties you
59
74
  // want to expose to search queries. List only
60
75
  // the fields which make sense to you.
61
76
  keys: ['productName', 'model', 'status'],
62
77
 
63
- // optional: default is false
78
+ // Optional: default is false
64
79
  caseSensitive: true,
65
80
 
66
- // optional: default is true
81
+ // Optional: default is true
67
82
  //
68
83
  // You should only ever consider disabling
69
84
  // memoization if you observe browser memory
@@ -72,7 +87,7 @@ const initSearchConfig = () => {
72
87
  }
73
88
 
74
89
  // Call the imported method with config arg.
75
- searchSetConfig(config)
90
+ searchSetConfig(config),
76
91
 
77
92
  // After setting the config, you can call searchHandler()
78
93
  // as many times as necessary without having to set the
@@ -86,23 +101,27 @@ const initSearchConfig = () => {
86
101
  //
87
102
  // Passing an empty string effectively returns the
88
103
  // the unfiltered set of records, the same as MyData.
89
- myFilteredData = <ItemRecord[]>searchHandler('')
104
+ myFilteredData = <ItemRecord[]>searchHandler(''),
105
+
106
+ // OR
107
+
108
+ myFilteredData = <MapRecords>searchHandler(''),
90
109
  }
91
- ````
110
+ ```
92
111
 
93
112
  ### 3. Initialize the search engine.
94
113
 
95
- This method (from Step 2.) should be called when your component has been
96
- initialized and your data is ready.
114
+ This method (from Step 2.) should be called when your component is being
115
+ initialized and when your data is ready.
97
116
 
98
- Also, this `initSearchConfig()` method should be called whenever your
117
+ Also, this `initSearchConfig()` method should be called again whenever your
99
118
  data changes after a record has been created, edited, or deleted.
100
119
 
101
120
  ```javascript
102
121
  // For this example we chose this method name; you
103
122
  // can name this wrapper method anything you want.
104
123
  initSearchConfig()
105
- ````
124
+ ```
106
125
 
107
126
  ### 4. Execute search queries and get results.
108
127
 
@@ -110,9 +129,13 @@ Presumably, a method is binded to the UI's search field (in this example it's ca
110
129
  `searchMyData`) and is called on every keystroke, debounced keystrokes, or an explicit
111
130
  and separate submit event. Ultimately, the UX behavior is left up to the front end
112
131
  engineer / design team.
132
+
113
133
  ```javascript
114
134
  const searchMyData = (query: string) => {
115
135
  // Call the imported method and get results.
136
+ //
137
+ // MyFilteredData is binded to the template for filtered display.
138
+ // Use the variable name and appropriate syntax for your app.
116
139
  myFilteredData = searchHandler(query)
117
140
  }
118
141
  ```
@@ -125,10 +148,11 @@ into several apps I had previously created with Vue using its Composition API.
125
148
  In this documentation I tried to keep the Usage code snippets framework neutral.
126
149
 
127
150
  `search-gold` itself is framework neutral and should work in any TS codebase.
151
+
128
152
  # Credits
129
153
 
130
154
  G. Gold
131
155
 
132
- October 2025
156
+ February 2026
133
157
 
134
158
  Have fun!
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "search-gold",
3
3
  "private": false,
4
- "version": "1.0.2",
5
- "description": "speedy search engine targeting an array of objects",
4
+ "version": "1.1.0",
5
+ "description": "fast search engine targeting array of objects or Map",
6
6
  "author": "gold",
7
7
  "main": "./src/search.ts",
8
8
  "type": "module",
@@ -14,14 +14,14 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@typescript-eslint/eslint-plugin": "^8.34.0",
17
- "@typescript-eslint/parser": "^8.34.0",
18
- "eslint": "9.33.0",
19
- "eslint-plugin-oxlint": "1.12.0",
20
- "globals": "^16.2.0",
21
- "oxlint": "1.12.0",
22
- "prettier": "^3.5.3",
23
- "tslib": "^2.8.1",
24
- "typescript-eslint": "8.39.1"
17
+ "@typescript-eslint/parser": "8.56.0",
18
+ "eslint": "9.39.2",
19
+ "eslint-plugin-oxlint": "1.49.0",
20
+ "globals": "17.3.0",
21
+ "oxfmt": "0.34.0",
22
+ "oxlint": "1.49.0",
23
+ "tslib": "2.8.1",
24
+ "typescript-eslint": "8.56.0"
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
package/src/search.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  export type ItemRecord = Record<string, any>
2
+ export type MapRecords = Map<string, ItemRecord>
2
3
 
3
4
  export interface ConfigParams {
4
- items: ItemRecord[]
5
+ items: ItemRecord[] | MapRecords
5
6
  keys: string[]
6
7
  caseSensitive?: boolean
7
8
  enableMemoize?: boolean
8
9
  }
9
10
 
10
11
  interface Config {
11
- Items: ItemRecord[]
12
+ Items: ItemRecord[] | MapRecords
12
13
  Keys: string[]
13
14
  RegexFlag: 'i' | undefined
14
15
  EnableMemoize: boolean
@@ -25,6 +26,8 @@ const escapeRegExp = (str: string): string => {
25
26
  const cache = new Map()
26
27
 
27
28
  const Conf: Config = {
29
+ // Items could be either an array or a map.
30
+ // run-time will handle detection and applicable syntax.
28
31
  Items: [],
29
32
  Keys: [],
30
33
  RegexFlag: 'i',
@@ -49,25 +52,36 @@ export const searchSetConfig = (params: ConfigParams): void => {
49
52
  // that it behooves the caller to this method be responsible for
50
53
  // correctly setting the config for this module and to check that the
51
54
  // search's query argument is a string.
52
- export const searchHandler = (query: string): ItemRecord[] => {
53
- const results: ItemRecord[] = []
55
+ export const searchHandler = (query: string): ItemRecord[] | MapRecords => {
54
56
  const qTrimmed = escapeRegExp(query.trim())
55
57
 
56
58
  if (qTrimmed === '') {
57
59
  return Conf.Items
58
60
  }
59
61
 
62
+ let results: ItemRecord[] | MapRecords
63
+
60
64
  if (Conf.EnableMemoize && cache.has(qTrimmed)) {
61
65
  return cache.get(qTrimmed)
62
66
  }
63
67
 
64
68
  const rx = new RegExp(qTrimmed, Conf.RegexFlag)
65
69
 
66
- // All the dirty work is done here...
67
- for (const item of Conf.Items) {
68
- const searchString = Conf.Keys.map((k) => item[k]).join('^')
69
- if (rx.test(searchString)) {
70
- results.push(item)
70
+ if (Conf.Items instanceof Map) {
71
+ results = <MapRecords>new Map()
72
+ for (const [key, item] of Conf.Items) {
73
+ const searchString = Conf.Keys.map((k) => item[k]).join('^')
74
+ if (rx.test(searchString)) {
75
+ results.set(key, item)
76
+ }
77
+ }
78
+ } else {
79
+ results = <ItemRecord[]>[]
80
+ for (const item of Conf.Items) {
81
+ const searchString = Conf.Keys.map((k) => item[k]).join('^')
82
+ if (rx.test(searchString)) {
83
+ results.push(item)
84
+ }
71
85
  }
72
86
  }
73
87