Security News
New Python Packaging Proposal Aims to Solve Phantom Dependency Problem with SBOMs
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
comparator-factory-factory
Advanced tools
Create comparison functions to be used for sorting arrays.
Create comparison functions to be used for sorting arrays.
This is a dedicated tiny library, not aiming at a framework.
undefined
, null
, NaN
as first or lastIntl.Collator
$ npm install comparator-factory-factory
import comparatorFactoryFactory from "comparator-factory-factory";
// const comparatorFactoryFactory = require("comparator-factory-factory");
const comparing = comparatorFactoryFactory();
[].sort(comparing());
<script src="https://cdn.jsdelivr.net/npm/comparator-factory-factory@0.2.1"></script>
<script>
const comparing = comparatorFactoryFactory();
[].sort(comparing());
</script>
or for modern browsers:
<script type="module">
import comparatorFactoryFactory from "https://cdn.jsdelivr.net/npm/comparator-factory-factory@0.2.1/index.min.mjs";
const comparing = comparatorFactoryFactory();
[].sort(comparing());
</script>
const comparing = comparatorFactoryFactory({
specials: [[null, "last"]],
collator: { caseFirst: "upper", numeric: true },
});
["A5", "A1", null, "A3", "A10", "a3", "A7"].sort(comparing());
// => ["A1", "A3", "a3", "A5", "A7", "A10", null]
["A5", "A1", null, "A3", "A10", "a3", "A7"].sort(comparing().reversed());
// => [null, "A10", "A7", "A5", "a3", "A3", "A1"]
const users = [
{ id: "01", name: "Alice", profile: { age: 17 } },
{ id: "02", name: "Bob" },
{ id: "03", profile: { age: 16 } },
{ id: "04", name: "alice", profile: { age: 15 } },
{ id: "05", name: "bob", profile: { age: 18 } },
{ id: "06", name: "Bob", profile: { age: 15 } },
];
{
const comparing = comparatorFactoryFactory();
users.sort(comparing(x => [x.profile.age, x.id]));
// => [
// { id: "02", name: "Bob" },
// { id: "04", name: "alice", profile: { age: 15 } },
// { id: "06", name: "Bob", profile: { age: 15 } },
// { id: "03", profile: { age: 16 } },
// { id: "01", name: "Alice", profile: { age: 17 } },
// { id: "05", name: "bob", profile: { age: 18 } },
// ]
}
{
const comparing1 = comparatorFactoryFactory({
specials: [[undefined, "last"]],
collator: { sensitivity: "base" },
});
const comparing2 = comparatorFactoryFactory({
specials: [[undefined, "last"]],
});
users.sort(
comparing1(x => x.name)
.reversed()
.or(comparing2(x => x.profile.age))
.or(comparing2(x => x.id))
);
// => [
// { id: "03", profile: { age: 16 } },
// { id: "06", name: "Bob", profile: { age: 15 } },
// { id: "05", name: "bob", profile: { age: 18 } },
// { id: "02", name: "Bob" },
// { id: "04", name: "alice", profile: { age: 15 } },
// { id: "01", name: "Alice", profile: { age: 17 } },
// ]
}
{
const comparingPropertyPath = comparatorFactoryFactory({
selector(fullpath) {
const paths = fullpath.replace(/\[(\d+)]/g, ".$1").split(".").filter(Boolean);
return obj => paths.every(path => (obj = obj[path]) != null) && obj;
},
});
users.sort(comparingPropertyPath("profile.age", "id"));
// => [
// { id: "02", name: "Bob" },
// { id: "04", name: "alice", profile: { age: 15 } },
// { id: "06", name: "Bob", profile: { age: 15 } },
// { id: "03", profile: { age: 16 } },
// { id: "01", name: "Alice", profile: { age: 17 } },
// { id: "05", name: "bob", profile: { age: 18 } },
// ]
}
// Create a comparison function factory based on the specified rule.
const comparatorFactory = comparatorFactoryFactory({
selector: key => obj => comparisonResult,
specials: [
[undefined, "first"], // array with 2 elements:
[null, "first"], // [0] value to treat specially
[NaN, "first"], // [1] "first" / "last"
],
collator: {
locales: undefined, // a BCP 47 language tag, or an array of such strings
sensitivity: "variant", // "base" / "accent" / "case" / "variant"
numeric: false,
caseFirst: "false", // "upper" / "lower" / "false" (use the locale's default)
},
});
// Create a comparison function.
const comparator = comparatorFactory(key1, key2, ...);
// Evaluate.
// 0 if obj1 and obj2 are equal,
// a negative number if obj1 is smaller,
// a positive number if obj1 is larger.
const comparisonResult = comparator(obj1, obj2);
// Create a comparison function with reverse order.
const reversedComparator = comparator.reversed();
// Comparator itself.
const comparatorItself = comparator.reversed(false);
// Create a combined comparison function.
// If comparator(obj1, obj2) is falsy, then evaluate specified comparison function.
const combinedComparator = comparator.or((obj1, obj2) => number);
Create a comparison function factory based on the specified rule.
selector: key => obj => comparisonResult
A function selecting comparison value from key
and obj
.
The receiving parameter key
is each argument of comparatorFactory(key1, key2, ...)
.
The receiving parameter obj
is each argument of comparator(obj1, obj2)
.
The default implementation is as follows.
key => obj => {
try {
return key(obj);
} catch {
return undefined;
}
}
Following code is a property-path-based comparison example using lodash/get. (BTW, there are so many similar modules.)
const get = require("lodash/get");
const comparingPropertyPath = comparatorFactoryFactory({
selector: key => obj => get(obj, key),
});
// for TypeScript:
// const comparingPropertyPath = comparatorFactoryFactory<string>({
// selector: key => obj => get(obj, key),
// });
const users = [
{ id: 1, profile: { age: 18 } },
{ id: 2, profile: { age: 15 } },
];
users.sort(comparingPropertyPath("profile.age", "id"));
specials: [[value1, "first" (or "last")], [value2, "first" (or "last")], ...]
Special values to place first or last.
The default value is as follows.
[
[undefined, "first"],
[null, "first"],
[NaN, "first"],
]
collator: { locales?, sensitivity?, numeric?, caseFirst? } | { compare: (string1, string2) => number }
String comparison method.
Possible values are as follows.
Intl.Collator
constructor with optional property locales
compare(string1, string2) => number
method (a Intl.Collator
instance does)See Intl.Collator at MDN for details.
The default value is the default Intl.Collator()
.
Create a comparison function.
key1
, key2
, ...
Comparison key passed to the selector
option of the rule.
If the length of arguments is 0, obj
itself becomes the comparison value.
Evaluate.
0 if obj1
and obj2
are considered to be equal,
a negative number if obj1
is considered to be smaller than obj2
,
a positive number if obj1
is considered to be larger than obj2
.
Create a comparison function with reverse order.
Create a combined comparison function.
The combined comparison function evaluates the original comparator(obj1, obj2)
first.
If that result equals to 0 (or falsy), it evaluates specified comparison function next.
Array.prototype.sort()
always put the undefined
at the end of the array.
This behavior is specified in the ECMAScript specification.
const comparing = comparatorFactoryFactory({
specials: [[undefined, "first"], [null, "first"], [NaN, "first"]],
});
[{ id: 3 }, { id: 1 }, { id: undefined }, { id: 7 }].sort(comparing(x => x.id));
// => [{ id: undefined }, { id: 1 }, { id: 3 }, { id: 7 }]
// As expected.
[3, 1, null, 7].sort(comparing());
// => [null, 1, 3, 7]
// As expected.
[3, 1, NaN, 7].sort(comparing());
// => [NaN, 1, 3, 7]
// As expected.
[3, 1, undefined, 7].sort(comparing());
// => [1, 3, 7, undefined]
// NOT as expected.
// The expected result is [undefined, 1, 3, 7]
// but `undefined` is always placed at the end...
WTFPL
FAQs
Create comparison functions to be used for sorting arrays.
The npm package comparator-factory-factory receives a total of 7 weekly downloads. As such, comparator-factory-factory popularity was classified as not popular.
We found that comparator-factory-factory demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
Security News
Socket CEO Feross Aboukhadijeh discusses open source security challenges, including zero-day attacks and supply chain risks, on the Cyber Security Council podcast.
Security News
Research
Socket researchers uncover how threat actors weaponize Out-of-Band Application Security Testing (OAST) techniques across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.