Skip to content

Commit b4532d1

Browse files
authored
Merge pull request #6 from Feresaul/setup
Setup build - Workflow config - Docs
2 parents add2c55 + 0bdb608 commit b4532d1

9 files changed

Lines changed: 2360 additions & 99 deletions

File tree

.github/workflows/verify-build.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Verify Build
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v3
15+
16+
- name: Set up Node.js
17+
uses: actions/setup-node@v3
18+
with:
19+
node-version: 20
20+
21+
- name: Install dependencies
22+
run: npm install
23+
24+
- name: Run build
25+
run: npm run build
26+
27+
- name: Run tests
28+
run: npm test

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
node_modules
1+
node_modules
2+
dist

README.md

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,146 @@
1-
# deep-key
1+
# @nuc-lib/deep-key
2+
3+
## Description
4+
5+
`@nuc-lib/deep-key` is a utility library designed to simplify working with deeply nested objects in JavaScript. This is intended to be used when you work with dynamic keys or when you are not sure if the property exists. It provides a set of functions so you don't have to deal with the complexity of checking for the existence of properties at each level of the object.
6+
7+
## Features
8+
9+
- Safely access deeply nested properties.
10+
- Filter arrays based on nested properties.
11+
- Sort arrays based on nested properties.
12+
- Lightweight and easy to integrate.
13+
14+
## Usage
15+
16+
### Accessing Nested Properties
17+
18+
The `getKeyValue` utility lets you access nested properties using a dot notation string. If the property does not exist, it returns `undefined` instead of throwing an error.
19+
20+
The key is a string that represents the path to the property you want to access. The path is defined using dot notation, where each level of the object is separated by a dot.
21+
22+
```javascript
23+
import { getKeyValue } from '@nuc-lib/deep-key';
24+
25+
const guy = {
26+
id: 2,
27+
personalInfo: { name: 'John Doe', age: 12, city: 'New York' },
28+
contacts: [
29+
{ name: 'Jane Doe', email: 'afk@example.com' },
30+
{ name: 'Alice Smith', email: 'alice@example.com' },
31+
{ name: 'Bob Johnson', email: 'bob@example.com' },
32+
{ name: 'Charlie Brown', email: 'charlie@example.com' }
33+
],
34+
associatedIds: [23, 43, 67, 89]
35+
};
36+
37+
getKeyValue(guy, 'personalInfo.name'); // 'John Doe'
38+
getKeyValue(guy, 'personalInfo.age'); // 12
39+
getKeyValue(guy, 'personalInfo.city'); // 'New York'
40+
getKeyValue(guy, 'personalInfo.active'); // undefined -> no error thrown
41+
```
42+
43+
For arrays there are two ways to access the values:
44+
45+
- Getting an element in a specific index.
46+
47+
```javascript
48+
getKeyValue(guy, 'contacts.0');
49+
// { name: 'Jane Doe', email: 'afk@example.com' }
50+
getKeyValue(guy, 'contacts.0.name'); // 'Jane Doe'
51+
getKeyValue(guy, 'contacts.0.email'); // 'afk@example.com'
52+
```
53+
54+
- Getting all the values in the array. A key wrapped in `[]` represents an actual mapping over the array, this means it will return an array of values from the parent array.
55+
56+
> **Note:** There is no need to use the `[]` notation if you are not using TypeScript. The utility will still map over the array and return the values. This is just a visual aid to show that the key is an array to be mapped.
57+
58+
```javascript
59+
getKeyValue(guy, '[contacts].name');
60+
// ['Jane Doe', 'Alice Smith', 'Bob Johnson', 'Charlie Brown']
61+
getKeyValue(guy, 'contacts.name');
62+
// ['Jane Doe', 'Alice Smith', 'Bob Johnson', 'Charlie Brown'] -> Works the same as above
63+
getKeyValue(guy, '[contacts].invalidKey');
64+
// [undefined, undefined, undefined, undefined] -> no error thrown
65+
```
66+
67+
### Filtering By Nested Properties
68+
69+
The `filterByKeyValue` utility allows you to filter an array of objects based on a nested property. It returns a new array containing only the objects that match the specified key and value.
70+
71+
```javascript
72+
import { filterByKeyValue } from '@nuc-lib/deep-key';
73+
74+
const people = [
75+
{
76+
id: 1,
77+
name: 'John Doe',
78+
age: 25,
79+
parentIds: [1, 2],
80+
address: { city: 'Houston', zip: '10001' }
81+
},
82+
{
83+
id: 2,
84+
name: 'Jane Doe',
85+
age: 30,
86+
parentIds: [3, 4],
87+
address: { city: 'Los Angeles', zip: '90001' }
88+
},
89+
{
90+
id: 3,
91+
name: 'Alice Smith',
92+
age: 22,
93+
parentIds: [5, 6],
94+
address: { city: 'Chicago', zip: '60601' }
95+
},
96+
{
97+
id: 4,
98+
name: 'Bob Johnson',
99+
age: 28,
100+
parentIds: [7, 8],
101+
address: { city: 'Houston', zip: '77001' }
102+
}
103+
];
104+
105+
filterByKeyValue(people, 'age', 25);
106+
// [ { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } } ]
107+
108+
filterByKeyValue(people, 'address.city', 'Houston');
109+
// [
110+
// { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } },
111+
// { id: 4, name: 'Bob Johnson', age: 28, parentIds: [7, 8], address: { city: 'Houston', zip: '77001' } }
112+
// ]
113+
filterByKeyValue(people, 'address', 'Houston');
114+
// [] -> no error thrown
115+
```
116+
117+
Strict mode for filter arrays is also supported. This means that the filter will only return the objects that match the exact value of the key.
118+
119+
```javascript
120+
filterByKeyValue(people, 'age', [22, 25]);
121+
// [
122+
// { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } },
123+
// { id: 3, name: 'Alice Smith', age: 22, parentIds: [5, 6], address: { city: 'Chicago', zip: '60601' } },
124+
// ]
125+
```
126+
127+
You can do a **loose filter** by passing `false` as the fourth argument.
128+
This means that the filter will return the objects that match any of the values in the array.
129+
130+
```javascript
131+
filterByKeyValue(people, 'age', [22, 25], false);
132+
// [
133+
// { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } },
134+
// { id: 3, name: 'Alice Smith', age: 22, parentIds: [5, 6], address: { city: 'Chicago', zip: '60601' } },
135+
// ]
136+
```
137+
138+
Use a custom filter function by passing a function as the third argument.
139+
140+
```javascript
141+
filterByKeyValue(people, 'age', (value) => value > 25);
142+
// [
143+
// { id: 2, name: 'Jane Doe', age: 30, parentIds: [3, 4], address: { city: 'Los Angeles', zip: '90001' } },
144+
// { id: 4, name: 'Bob Johnson', age: 28, parentIds: [7, 8], address: { city: 'Houston', zip: '77001' } }
145+
// ]
146+
```

lib/utils/filter.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,60 @@ import { getKeyValue } from './value';
22

33
import type { DeepKeyOf, TObject } from '../types';
44

5+
type FilterValue = string | number | boolean | string[] | number[];
6+
57
/**
68
* This function filters an array of objects based on a specific key and value.
79
* It returns a new array containing only the objects that match the given key-value pair.
810
* @param list The array of objects to filter.
911
* @param key The key to filter by.
10-
* @param value The value to match against the specified key.
12+
* @param filter The value to filter by. This can be a single value or an array of values.
13+
* It also supports a function that takes the value of the key and returns a boolean.
14+
* @param mode The mode of filtering. 'strict' checks for exact matches, while 'loose' allows for partial matches.
15+
* Only applies to array filter values.
16+
* This is ignored if custom filter function is provided.
1117
* @returns A new array of objects that match the specified key-value pair.
1218
*/
13-
export const filterArrayByKeyValue = <T extends TObject>(
19+
export const filterByKeyValue = <T extends TObject>(
1420
list: T[],
1521
key: DeepKeyOf<T>,
16-
value: string | number | boolean | string[] | number[]
22+
filter: ((value: FilterValue) => boolean) | FilterValue,
23+
strict = true
1724
) => {
1825
return list.filter((item) => {
19-
if (Array.isArray(value)) {
20-
if (value.length === 0) {
26+
const itemKeyValue = getKeyValue(item, key);
27+
// Early return if itemKeyValue is undefined or an object
28+
if (!itemKeyValue) {
29+
return false;
30+
}
31+
// Verify if filter is a function and call it with itemKeyValue
32+
if (typeof filter === 'function') {
33+
try {
34+
return filter(itemKeyValue);
35+
} catch {
2136
return false;
2237
}
23-
return value.some((val) => val === getKeyValue(item, key));
2438
}
25-
if (value) {
26-
return true;
39+
// If filter is an array, check if itemKeyValue is in the array
40+
if (Array.isArray(filter)) {
41+
if (filter.length === 0) {
42+
return false;
43+
}
44+
45+
if (Array.isArray(itemKeyValue)) {
46+
if (strict) {
47+
return filter.every((val) => itemKeyValue.includes(val));
48+
}
49+
return filter.some((val) => itemKeyValue.includes(val));
50+
}
51+
52+
return filter.some((val) => val === itemKeyValue);
2753
}
28-
return getKeyValue(item, key) === value;
54+
// If key value is an array check if filter is in the array
55+
if (Array.isArray(itemKeyValue)) {
56+
return itemKeyValue.some((val) => val === filter);
57+
}
58+
59+
return itemKeyValue === filter;
2960
});
3061
};

lib/utils/sort.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { DeepKeyOf, TObject } from '../types';
99
* @param order the order to sort by. Available values are 'ASC' and 'DESC'.
1010
* @returns an array sorted by the given key and order.
1111
*/
12-
export const sortByProp = <T extends TObject>(
12+
export const sortByKeyValue = <T extends TObject>(
1313
array: T[],
1414
key: DeepKeyOf<T>,
1515
order: 'ASC' | 'DESC' = 'ASC'

0 commit comments

Comments
 (0)