-
Notifications
You must be signed in to change notification settings - Fork 1
Home
jericirenej edited this page May 29, 2022
·
9 revisions
Easily filter object by their properties - then get the filtered object back!
- Two types of filtering available:
- Exclude all of the matched properties or
- Include only the matched properties.
- Filter by a list of property names or regex filters.
- Recursive filtering option.
- Recursive filtering will not inspect (filter within) arrays, sets, maps, dates, and other in-built classes.
- Compatibility with ES6 and CommonJS imports.
- Improved inclusive filtering: you no longer have to specify all the properties that lead to the target property to be included. Specify only those that you want to keep and the algorithm does the rest!
- Filtering is now recursive by default, if not specified otherwise.
- Compatibility with ES6 and CommonJS module systems.
- Completely reworked filtering logic.
- Install the package via
npm install @jericirenej/object-filter
.- You can also clone the repo and transpile the files manually (
npm install
, followed bynpm run compile
which will transpile todist
).
- You can also clone the repo and transpile the files manually (
- Import or require the
objectFilter
function from@jericirenej/object-filter
and pass it the appropriate configuration object.
// ES6 import
import objectFilter from "@jericirenej/object-filter"
// CommonJS import
const objectFilter = require("@jericirenej/object.filter").default;
/*
objectFilter signature: {
targetObject: Record<string,any>,
filters?: string|string[],
regexFilters?: string|RegExp|(string|RegExp)[], --- At least one of the filter groups needs to be valid.
filterType?: "exclude"|"include", --- defaults to exclude.
recursive?:boolean, --- defaults to true.
}
*/
const employeeInfo = {
name: "John",
surname: "Doe",
personalInfo: {
age: 30,
sensitive1: "secret",
},
sensitive2: "secret",
};
const excludeSensitive = {
targetObject: employeeInfo,
regexFilters: /sensitive/,
filterType: "exclude",
recursive: true,
};
const cleanedEmployeeInfo = objectFilter(excludeSensitive);
// Will return
{
name: "John",
surname: "Doe",
personalInfo: {
age: 30,
},
};
const includeSensitive = {
targetObject: employeeInfo,
regexFilters: /sensitive/,
filterType: "include",
recursive: true,
}
const sensitiveEmployeeInfo = objectFilter(includeSensitive);
//Will return
{
sensitive2: "secret",
personalInfo: {
sensitive1: "secret,
}
}
- At each filter level, keeps all of the properties that do not match any of the provided filters.
- Nested properties are then filtered again upon recursive calls.
Can be summed up as being conditionally greedy / granular.
- It will by default include the complete value of the matched property. So, if a property is itself an object, it will include that complete sub-object.
- However, in recursive contexts:
- If any of the nested properties themselves match, then only the matched properties will be included.
- If none of the nested properties match, then all of them will remain included.
In other words: At a single level, only matched properties are included, but they are included in a comprehensive way. This is then replayed down each level. The more filters you supply, the more granular and selective your included output will be.
For example, let's say we have the following object:
{
unmatchedProp1: "primitive",
unmatchedProp2: "primitive",
targetProp: {
potentiallyMatched: "primitive",
unmatchedProp3: "primitive"
}
}
If we run objectFilter
with "include" filterType and only the targetProp
filter, the return will be:
{
targetProp: {
potentiallyMatched: "primitive",
unmatchedProp3: "primitive"
}
}
However, if we run it with the ["targetProp", "potentiallyMatched"]
filter (or equivalent regexFilters), the return will be:
{
targetProp: {
potentiallyMatched: "primitive",
}
}
- Non-recursive filtering only checks the top-level object properties ad will return those that pass the checks. This means, that if some of the object properties are themselves objects, they can contain properties that should have been filtered out.
- Also, since assignment takes place at the top-level only, this means that all of the property values from the returned object, that are not primitives, should be considered shallow. Modifying them will modify the original object as well.
Primitives and excluded object types are not subject to filtering.
- The following types fall under primitives:
string, number, boolean, bigint, symbol, undefined, null
.- While
typeof null
returns an object, it is specified as a primitive in the ECMAScript docs and it also doesn't have any properties to filter by.
- While
- The following are considered excluded types:
Array, Map, Set, Date, WeakMap, WeakSet, Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Uint8ClampedArray, BigInt64Array, BigUint64Array, ArrayBuffer, SharedArrayBuffer
- Although the filterObject function creates a new object based on the old one, it is not guaranteed to produce a deep clone of the object. In particular, excluded types (for example arrays, sets, maps, etc.) will be shallow copied. If you need to ensure immutability, use an appropriate library (
lodash
,immer
), to clone the result. - You need to supply at least one type of valid filter groups. If none of the supplied filter groups are valid, the original object will be returned.
- You can supply both filter types if you want, combining string based filters with regex patterns.
- String based filters, passed under the
filters
argument, are implemented by running aninclude
method on the source object property names (so more inclusive than a strict equality comparison).
- If you are working with
TypeScript
, make sure to type cast the returned object, as its type will beRecord<string,any>
by default.
- Tranpoline recursive calls (could be useful for very deeply nested objects so as not to exceed the maximum call stack).
- GitHub Page for documentation with a live code playground for testing out the package.