@hotequil/proposal-filter-groups
v0.1.2
Published
TC39 proposal to implement the Array.prototype.filterGroups.
Maintainers
Readme
Filter groups proposal
TC39 proposal to implement the Array.prototype.filterGroups.

Reason
The motivation behind this proposal is to simplify and enhance the process of filtering arrays into multiple distinct groups within JavaScript. Currently, filtering items based on various conditions typically involves multiple calls to Array.prototype.filter, which can result in verbose, less readable and potentially less efficient code.
While Object.groupBy offers a grouping mechanism, it relies on a single callback function that categorizes items into groups. This approach may reduce code clarity, especially when handling multiple, complex filtering criteria.
This proposal introduces Array.prototype.filterGroups, a method that accepts multiple callback functions and returns an array of filtered groups accordingly. This method can be invoked directly on any array instance, promoting cleaner and more expressive code.
JavaScript provides a rich set of Array methods. However, when multiple, differentiated filters are needed, developers often resort to external libraries, custom helper functions or repetitive filter calls. This proposal seeks to offer a native, elegant solution to this common use case.
I've developed this proposal with the intention to contribute to the JavaScript community and the language specification. You can see the package in npm.
How it works
- It receives one or more functions (callbacks) that return a boolean;
- It returns the filtered arrays inside a parent array.
Installation
Install the package using npm or another package manager you want.
npm install @hotequil/proposal-filter-groupsUsage
Import the polyfill in the main, index or app file of your project.
import "@hotequil/proposal-filter-groups";Typical cases
Follow the examples in TypeScript below.
// Imports omitted…
export const vehicles: Vehicle[] = [
{ name: "Toyota Corolla", type: "sedan" },
{ name: "Honda Fit", type: "hatch" },
{ name: "Honda Civic", type: "sedan" },
{ name: "Honda CRV", type: "suv" },
{ name: "Toyota Etios", type: "hatch" },
{ name: "Honda Odyssey", type: "van" },
{ name: "Toyota Dyna", type: "truck" },
{ name: "Toyota SW4", type: "suv" }
];
const [sedanVehicles, hatchVehicles, suvVehicles, otherVehicles] =
vehicles.filterGroups(
// You can use the index and array parameters too, it helps to mix many conditions
({ type }, _index, _array) => type === "sedan",
vehicle => vehicle.type === "hatch",
vehicle => vehicle.type === "suv"
);
// [
// [
// { name: "Toyota Corolla", type: "sedan" },
// { name: "Honda Civic", type: "sedan" }
// ],
// [
// { name: "Honda Fit", type: "hatch" },
// { name: "Toyota Etios", type: "hatch" }
// ],
// [
// { name: "Honda CRV", type: "suv" },
// { name: "Toyota SW4", type: "suv" }
// ],
// [
// { name: "Honda Odyssey", type: "van" },
// { name: "Toyota Dyna", type: "truck" }
// ]
// ]
// The first callbacks have preference
const [vehiclesWithName, vehiclesWithType] =
vehicles.filterGroups(
({ name }) => name.length > 0,
({ type }) => type.length > 0
);
// [
// [
// { name: "Toyota Corolla", type: "sedan" },
// { name: "Honda Fit", type: "hatch" },
// { name: "Honda Civic", type: "sedan" },
// { name: "Honda CRV", type: "suv" },
// { name: "Toyota Etios", type: "hatch" },
// { name: "Honda Odyssey", type: "van" },
// { name: "Toyota Dyna", type: "truck" },
// { name: "Toyota SW4", type: "suv" }
// ],
// []
// ]
// It'll throw a TypeError, it isn't allowed use numbers in callbacks
vehicles.filterGroups(
() => true,
() => true,
1,
() => true
)
// It'll throw a TypeError, it isn't allowed use strings in callbacks
vehicles.filterGroups(
() => true,
() => true,
"type",
() => true
)
// It'll throw a TypeError, it isn't allowed use booleans in callbacks
vehicles.filterGroups(
() => true,
() => true,
true,
() => true
)
// It'll throw a TypeError, it isn't allowed use objects in callbacks
vehicles.filterGroups(
() => true,
() => true,
{},
() => true
)
export const users: User[] = [
{ name: "Oliver", status: { name: "active" } },
{ name: "Henry", status: { name: "inactive" } },
{ name: "Thomas", status: { name: "active" } }
];
const [activeUsers, inactiveUsers] = users.filterGroups(
user => user.status.name === "active"
);
// [
// [
// { name: "Oliver", status: { name: "active" } },
// { name: "Thomas", status: { name: "active" } }
// ],
// [
// { name: "Henry", status: { name: "inactive" } }
// ]
// ]Performance
Test the performance.ts file.
const numbers = Array.from(
{ length: 1_000_000 },
(_, index) => index - 500_000
);
// 21.214ms
const [_negatives, _evens, _odds] = numbers.filterGroups(
number => number < 0,
number => number % 2 === 0,
number => number % 2 !== 0
);
// 31.29ms
const { _negatives1, _evens1, _odds1 } = Object.groupBy(numbers, number => {
if (number < 0) return "_negatives1";
if (number % 2 === 0) return "_evens1";
if (number % 2 !== 0) return "_odds1";
return "others"
});
// 31.463ms
const _negatives2 = numbers.filter(number => number < 0);
const _evens2 = numbers.filter(number => number >= 0 && number % 2 === 0);
const _odds2 = numbers.filter(number => number >= 0 && number % 2 !== 0);Similar methods
There are some similar methods, but they are not exactly the same as this proposal.
| Method name | Responsible | | ----------------------------------------------------------------------------------------------------------------------- | ----------- | | Object.groupBy | Native | | Array.prototype.filter | Native | | _.groupBy | Lodash | | _.partition | Lodash |
Proposer
- Author: @hotequil;
- Champion(s): no one at the moment.
This repository there isn't other third dependency, see the package.json.
