Skip to content

Commit 60a08c8

Browse files
Added benchmark
1 parent 9497f31 commit 60a08c8

File tree

4 files changed

+200
-24
lines changed

4 files changed

+200
-24
lines changed

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ you can simply write
2222

2323
[API](#api)
2424

25+
[Performance considerations](#performance-considerations)
26+
2527
[Security considerations](#security-considerations)
2628

2729
## Usage
@@ -139,7 +141,7 @@ Operator | Condition looks like | Meaning
139141
### compile
140142

141143
Compiles the given `selector`. The compiled selector can be passed to `apply`, `get`, and `set` instead of the original string.
142-
If you intend to re-use a given selector for multiple operations, pre-compiling it gives a performance boost.
144+
If you intend to re-use a given selector for multiple operations, pre-compiling it gives a significant performance boost.
143145

144146
The returned compiled selector also has methods `apply`, `get`, and `set`, so instead of calling `get(compiledSelector, obj)` you can
145147
also do `compiledSelector.get(obj)`.
@@ -217,6 +219,24 @@ Otherwise, this function follows the same rules as [apply](#apply).
217219
Returns **any** The new values of the selected properties. Unless collating, the length of the result gives an indication of
218220
how many properties matched the selector.
219221

222+
## Performance considerations
223+
224+
There are a few other libraries that do the same thing, although none offer the features and expressive powers of this one. In particular, none allow using wildcards in selectors, conditions, or application of arbitrary functions - that's why I wrote this in the first place. That power comes at a price, though, and that price is speed. Below are benchmark results comparing `object-selectors` with some of the competitors.
225+
226+
Library | ops/sec
227+
---|---:
228+
object-selectors (string selector) | 91,700 ops/sec ±10.38% (85 runs sampled)
229+
object-selectors (pre-compiled) | 1,041,568 ops/sec ±2.11% (90 runs sampled)
230+
[easy-object-selector](https://github.com/deltavi/easy-object-selector) | 4,080,250 ops/sec ±1.58% (93 runs sampled)
231+
[object-path](https://github.com/mariocasciaro/object-path) | 997,770 ops/sec ±6.47% (81 runs sampled)
232+
[dot-prop](https://github.com/sindresorhus/dot-prop) | 3,310,430 ops/sec ±8.16% (70 runs sampled)
233+
[pathval](https://github.com/chaijs/pathval) | 275,348 ops/sec ±4.44% (64 runs sampled)
234+
[getvalue](https://github.com/jonschlinkert/get-value) | 2,342,407 ops/sec ±3.62% (84 runs sampled)
235+
236+
The key take aways:
237+
1. If you only need to access simple nested properties in objects and performance is a concern, you should probably go with a different library.
238+
2. Pre-compiling a repeatedly-used selector gives a performance gain of about factor 100.
239+
220240
## Security considerations
221241

222242
This package is based on a technique called *parsing expression grammar* (PEG) using [Peggy](https://peggyjs.org/) to parse and evaluate selectors. It does not use `eval`, and is therefore safe from the security concerns around code injection arising from it. This means it is safe to pass user content as selectors for read-only operations.

bench.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Benchmark from 'benchmark';
2+
import { get, compile } from './applicators.js';
3+
import easyobjectselector from 'easy-object-selector';
4+
import objectpath from 'object-path';
5+
import * as dotprop from 'dot-prop';
6+
import * as pathval from 'pathval';
7+
import getvalue from 'get-value';
8+
9+
const selector = 'a.b.c.d';
10+
const compiled = compile(selector);
11+
const obj = { a: { b: { c: { d: 2 } } } }
12+
13+
new Benchmark.Suite('object-selectors vs. easy-object-selector')
14+
.add('object-selectors (string)', function() {
15+
get(selector, obj)
16+
})
17+
.add('object-selectors (pre-compiled)', function() {
18+
get(compiled, obj)
19+
})
20+
.add('easy-object-selector', function() {
21+
easyobjectselector.select(obj, selector);
22+
})
23+
.add('object-path', function() {
24+
objectpath.get(obj, selector);
25+
})
26+
.add('dot-prop', function() {
27+
dotprop.getProperty(obj, selector);
28+
})
29+
.add('pathval', function() {
30+
pathval.getPathValue(obj, selector);
31+
})
32+
.add('getvalue', function() {
33+
getvalue(obj, selector);
34+
})
35+
.on('cycle', function(event) {
36+
console.log(String(event.target));
37+
})
38+
.on('complete', function() {
39+
console.log('Fastest is ' + this.filter('fastest').map('name'));
40+
})
41+
.run();

package-lock.json

Lines changed: 132 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,22 @@
3535
"devDependencies": {
3636
"@peggyjs/eslint-parser": "^1.2.1",
3737
"@peggyjs/eslint-plugin": "^1.1.0",
38+
"benchmark": "^2.1.4",
3839
"chai": "^4.3.8",
3940
"documentation": "^14.0.2",
41+
"dot-prop": "^8.0.2",
42+
"easy-object-selector": "^1.3.0",
4043
"eslint": "^8.48.0",
4144
"eslint-plugin-chai-expect": "^3.0.0",
4245
"eslint-plugin-mocha": "^10.1.0",
4346
"esmock": "^2.3.8",
47+
"get-value": "^3.0.1",
4448
"gulp": "^4.0.2",
4549
"gulp-rename": "^2.0.0",
4650
"gulp-tap": "^2.0.0",
4751
"mocha": "^10.2.0",
52+
"object-path": "^0.11.8",
53+
"pathval": "^2.0.0",
4854
"peggy": "^3.0.2",
4955
"sinon": "^15.2.0",
5056
"sinon-chai": "^3.7.0"

0 commit comments

Comments
 (0)