Last active
July 18, 2024 15:02
-
-
Save jherax/f11d669ba286f21b7a2dcff69621eb72 to your computer and use it in GitHub Desktop.
Filters an array of objects with multiple match-criteria.
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
type FilterOperator = 'AND' | 'OR'; | |
type FiltersBy<T> = { | |
[K in keyof T]?: (value: T[K]) => boolean; | |
}; | |
/** | |
* Factory function that creates a specialized function to filter | |
* arrays, by validating all filters (AND operator), | |
* or validating just one of the filters (OR operator). | |
* @param operator Method to validate all filters: AND, OR | |
* @returns Specialized function | |
*/ | |
function arrayFilterFactory(operator: FilterOperator) { | |
const method = operator === 'AND' ? 'every' : 'some'; | |
/** | |
* Filters an array of objects using custom predicates. | |
* @param array List of elements to filter | |
* @param filters Object with predicates as filters | |
* @returns Filtered array | |
*/ | |
return function filterArrayBy<T>(array: T[], filters: FiltersBy<T>): T[] { | |
const filterKeys = Object.keys(filters); | |
return array.filter(item => { | |
// validates filters (AND|OR operator) | |
return filterKeys[method](prop => { | |
return filters[prop](item[prop]); | |
}); | |
}); | |
}; | |
} | |
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
describe('Testing arrayFilterFactory()', () => { | |
const arrayFilterEvery = arrayFilterFactory('AND'); | |
const arrayFilterSome = arrayFilterFactory('OR'); | |
type Person = { | |
age: number; | |
name: string; | |
email: string; | |
nation: string; | |
}; | |
const users: Person[] = [ | |
{age: 28, name: 'John', email: 'johnson@mail.com', nation: 'USA'}, | |
{age: 38, name: 'Marlin', email: 'marlin@mail.com', nation: 'England'}, | |
{age: 35, name: 'Tom', email: 'tom@mail.com', nation: 'England'}, | |
{age: 28, name: 'Mark', email: 'mark@mail.com', nation: 'England'}, | |
]; | |
it('should filter an array of objects validating just one of the filters', () => { | |
// filters are applied using OR operator | |
const filters: FiltersBy<Person> = { | |
nation: country => country.toUpperCase() === 'USA', | |
age: age => age < 30, | |
}; | |
const expected: Person[] = [ | |
{age: 28, name: 'John', email: 'johnson@mail.com', nation: 'USA'}, | |
{age: 28, name: 'Mark', email: 'mark@mail.com', nation: 'England'}, | |
]; | |
const filtered = arrayFilterSome(users, filters); | |
expect(filtered).toStrictEqual(expected); | |
}); | |
it('should filter an array of objects validating all filters', () => { | |
// filters are applied using OR operator | |
const filters: FiltersBy<Person> = { | |
age: age => age < 30, | |
nation: country => country.toUpperCase() === 'USA', | |
}; | |
const expected: Person[] = [ | |
{age: 28, name: 'John', email: 'johnson@mail.com', nation: 'USA'}, | |
]; | |
const filtered = arrayFilterEvery(users, filters); | |
expect(filtered).toStrictEqual(expected); | |
}); | |
}); |
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
/** | |
* Filters an array of objects using custom predicates. | |
* @param array List of elements to filter | |
* @param filters Object with predicates as filters | |
* @returns Filtered array | |
*/ | |
function filterArrayBy<T>(array: T[], filters: FiltersBy<T>): T[] { | |
const filterKeys = Object.keys(filters); | |
return array.filter(item => { | |
// validates all filters (AND operator) | |
return filterKeys.every(prop => { | |
return filters[prop](item[prop]); | |
}); | |
}); | |
} | |
type FiltersBy<T> = { | |
[K in keyof T]?: (value: T[K]) => boolean; | |
}; |
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
type Product = { | |
name: string; | |
color: string; | |
size: number; | |
locations: string[]; | |
details: {length: number; width: number}; | |
}; | |
describe('Testing filterArrayBy()', () => { | |
it('should filter an array of products by multiple properties', () => { | |
const products: Product[] = [ | |
{ name: 'A', color: 'Blue', size: 50, locations: ['USA', 'Europe'], details: { length: 20, width: 70 } }, | |
{ name: 'B', color: 'Blue', size: 60, locations: [], details: { length: 20, width: 70 } }, | |
{ name: 'C', color: 'Black', size: 70, locations: ['Japan'], details: { length: 20, width: 71 } }, | |
{ name: 'D', color: 'Green', size: 50, locations: ['USA'], details: { length: 20, width: 71 } }, | |
]; | |
// filters are applied using AND operator | |
const filters: FiltersBy<Product> = { | |
size: size => size === 50 || size === 70, | |
color: color => ['blue', 'black'].includes(color.toLowerCase()), | |
locations: locations => !!locations.find(c => ['japan', 'usa'].includes(c.toLowerCase())), | |
details: details => details.length < 30 && details.width >= 70, | |
}; | |
const expected: Product[] = [ | |
{ name: 'A', color: 'Blue', size: 50, locations: ['USA', 'Europe'], details: { length: 20, width: 70 } }, | |
{ name: 'C', color: 'Black', size: 70, locations: ['Japan'], details: { length: 20, width: 71 } }, | |
]; | |
const filtered = filterArrayBy(products, filters); | |
expect(filtered).toStrictEqual(expected); | |
}); | |
}); |
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 value = <T>(v: T) => (typeof v === 'string' ? v.toUpperCase() : v); | |
/** | |
* Filters an array of objects using a list of valid values. | |
* @param array List of elements to filter | |
* @param filters Object whith lists of valid values | |
* @returns Filtered array | |
*/ | |
function filterArrayByEq<T extends {[key: string]: unknown}>( | |
array: T[], | |
filters: FiltersByEq<T>, | |
): T[] { | |
const filterKeys = Object.keys(filters); | |
return array.filter(item => { | |
// validates all filters (AND operator) | |
return filterKeys.every(key => { | |
// ignores empty filters | |
if (!filters[key].length) return true; | |
return filters[key].find(f => value(f) === value(item[key])); | |
}); | |
}); | |
} | |
type FiltersByEq<T> = { | |
[K in keyof T]?: T[K][]; | |
}; |
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
type Product = { | |
name: string; | |
color: string; | |
size: number; | |
}; | |
describe('Testing filterArrayByEq()', () => { | |
it('should filter an array of objects using a list of valid values', () => { | |
const products: Product[] = [ | |
{name: 'A', color: 'Blue', size: 50}, | |
{name: 'B', color: 'Blue', size: 60}, | |
{name: 'C', color: 'Black', size: 70}, | |
{name: 'D', color: 'Green', size: 50}, | |
]; | |
// Filters are applied using AND operator. | |
// Each filter uses a list of valid values, compared using === operator. | |
// String values are not case-sensitive. | |
const filters: FiltersByEq<Product> = { | |
color: ['BLUE', 'black'], | |
size: [70, 50], | |
}; | |
const expected: Product[] = [ | |
{name: 'A', color: 'Blue', size: 50}, | |
{name: 'C', color: 'Black', size: 70}, | |
]; | |
const filtered = filterArrayByEq(products, filters); | |
expect(filtered).toStrictEqual(expected); | |
}); | |
}); |
Hey dude, thanks for sharing! Your snippet is handy so I can solve my problem related to filtering data using multiple criteria
Great code!
@estherjsuh where you able to solve it? I have the same problem....
I made a small lib to solve this problem
https://github.com/circa10a/filter-object-array
I have an array of Sizes which is Size = ["40","XL","43","M"]
This is what my Products JSON File looks like :
[{
"productCatgoris": "womensfashion",
"productSubCatgoris": "Kamiz",
"productStoreName": "S-trade Store",
"productId": "27",
"productName": "Cotton Unstitched Salwar Kameez for Women - Pink",
"productImage": "https://images.pexels.com/photos/15388429/pexels-photo-15388429.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
"productPrice": "699",
"productDiscount": "10",
"offerStatus": false,
"productBrand": "Strade",
"ExpressDelivary": true,
"FreeDelivary": true,
"productStock": "60",
"productSold": "10",
"offerStartAndEnDate": "12-12-2021-01-04-2022",
"offerName": "",
"productDescription": ["Cotton Unstitched Salwar Kameez for Women - Pink"],
"productSpecipication": {
"Brand": "S-trade",
"size": ["XL", "M", "L", "S", "2XL"],
"gender": "Womens",
"Color": ["Pink"]
},
"productShipping": "Dhaka city !!!! Feel free to buy from this site as we promise you same day delivery of any purchased items. Sookh helps you to purchase any goods or service with a very few steps. If you ever find any obligation of returning any purchased item or items, please review our Return & Refund Policy.",
"productReviews": []
},
{
"productCatgoris": "womensfashion",
"productSubCatgoris": "Kamiz",
"productStoreName": "S-trade Store",
"productId": "28",
"productName": "Cotton Unstitched Salwar Kameez for Women - Pink",
"productImage": "https://images.pexels.com/photos/15388429/pexels-photo-15388429.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
"productPrice": "699",
"productDiscount": "10",
"offerStatus": false,
"productBrand": "Strade",
"ExpressDelivary": false,
"FreeDelivary": false,
"productStock": "60",
"productSold": "10",
"offerStartAndEnDate": "12-12-2021-01-04-2022",
"offerName": "",
"productDescription": ["Cotton Unstitched Salwar Kameez for Women - Pink"],
"productSpecipication": {
"Brand": "S-trade",
"size": ["41", "42", "43", "45"],
"gender": "Womens",
"Color": ["Pink"]
},
"productShipping": "Dhaka city !!!! Feel free to buy from this site as we promise you same day delivery of any purchased items. Sookh helps you to purchase any goods or service with a very few steps. If you ever find any obligation of returning any purchased item or items, please review our Return & Refund Policy.",
"productReviews": []
},
{
"productCatgoris": "womensfashion",
"productSubCatgoris": "Kaftan",
"productStoreName": "S-trade Store",
"productId": "29",
"productName": "Georgette Luxury Kaftan",
"productImage": "https://images.pexels.com/photos/15388429/pexels-photo-15388429.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
"productPrice": "699",
"productDiscount": "10",
"offerStatus": false,
"productBrand": "Strade",
"ExpressDelivary": true,
"FreeDelivary": true,
"productStock": "60",
"productSold": "10",
"offerStartAndEnDate": "12-12-2021-01-04-2022",
"offerName": "",
"productDescription": ["Georgette Luxury Kaftan"],
"productSpecipication": {
"Brand": "S-trade",
"size": ["41", "42", "43", "45"],
"gender": "Womens",
"Color": ["Pink"]
},
"productShipping": "Dhaka city !!!! Feel free to buy from this site as we promise you same day delivery of any purchased items. Sookh helps you to purchase any goods or service with a very few steps. If you ever find any obligation of returning any purchased item or items, please review our Return & Refund Policy.",
"productReviews": []
},
{
"productCatgoris": "womensfashion",
"productSubCatgoris": "Kaftan",
"productStoreName": "S-trade Store",
"productId": "30",
"productName": "Georgette Luxury Kaftan",
"productImage": "https://images.pexels.com/photos/15388429/pexels-photo-15388429.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
"productPrice": "699",
"productDiscount": "10",
"offerStatus": false,
"productBrand": "Strade",
"ExpressDelivary": false,
"FreeDelivary": false,
"productStock": "60",
"productSold": "10",
"offerStartAndEnDate": "12-12-2021-01-04-2022",
"offerName": "",
"productDescription": ["Georgette Luxury Kaftan"],
"productSpecipication": {
"Brand": "S-trade",
"size": ["41", "42", "43", "45"],
"gender": "Womens",
"Color": ["Pink"]
},
"productShipping": "Dhaka city !!!! Feel free to buy from this site as we promise you same day delivery of any purchased items. Sookh helps you to purchase any goods or service with a very few steps. If you ever find any obligation of returning any purchased item or items, please review our Return & Refund Policy.",
"productReviews": []
},
{
"productCatgoris": "womensfashion",
"productSubCatgoris": "Kamiz",
"productStoreName": "S-trade Store",
"productId": "31",
"productName": "Cotton Unstitched Salwar Kameez for Women - Pink",
"productImage": "https://images.pexels.com/photos/15388429/pexels-photo-15388429.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
"productPrice": "699",
"productDiscount": "10",
"offerStatus": false,
"productBrand": "Strade",
"ExpressDelivary": false,
"FreeDelivary": false,
"productStock": "60",
"productSold": "10",
"offerStartAndEnDate": "12-12-2021-01-04-2022",
"offerName": "",
"productDescription": ["Cotton Unstitched Salwar Kameez for Women - Pink"],
"productSpecipication": {
"Brand": "S-trade",
"size": ["40", "41", "42", "43", "45"],
"gender": "Womens",
"Color": ["Pink"]
},
"productShipping": "Dhaka city !!!! Feel free to buy from this site as we promise you same day delivery of any purchased items. Sookh helps you to purchase any goods or service with a very few steps. If you ever find any obligation of returning any purchased item or items, please review our Return & Refund Policy.",
"productReviews": []
},
]
I have to find all products based on a given size array. That size needs to match the productSpecipication > Size[]
array. if matched, it will return the particular products as a products array.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hey man! thank you a million!!! if you ever come to romania i'll buy you 10 beers!!!!!!!!!!!!!!!!!!!