Skip to content

Commit e758b76

Browse files
feat: add object/inverse-by
Ref: #8755
1 parent 9446418 commit e758b76

10 files changed

Lines changed: 1349 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
<!--
2+
3+
@license Apache-2.0
4+
5+
Copyright (c) 2018 The Stdlib Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
# Object Inverse
22+
23+
> Invert an object, such that keys become values and values become keys, according to a transform function.
24+
25+
<section class="usage">
26+
27+
## Usage
28+
29+
```javascript
30+
var invertBy = require( '@stdlib/object/inverse-by' );
31+
```
32+
33+
#### invertBy( obj, \[options,] transform )
34+
35+
Inverts an object, such that keys become values and values become keys, according to a transform function.
36+
37+
```javascript
38+
function transform( key, value ) {
39+
return value;
40+
}
41+
var obj = {
42+
'a': 'beep',
43+
'b': 'boop'
44+
};
45+
var out = invertBy( obj, transform );
46+
// returns { 'beep': 'a', 'boop': 'b' }
47+
```
48+
49+
The function accepts the following options:
50+
51+
- **duplicates**: boolean indicating whether to store keys mapped to duplicate values in arrays. Default: `true`.
52+
53+
By default, keys mapped to duplicate values are stored in arrays.
54+
55+
```javascript
56+
function transform( key, value ) {
57+
return value;
58+
}
59+
var obj = {
60+
'a': 'beep',
61+
'b': 'beep'
62+
};
63+
var out = invertBy( obj, transform );
64+
// returns { 'beep': [ 'a', 'b' ] }
65+
```
66+
67+
To **not** allow duplicates, set the `duplicates` option to `false`. The output key-value pair will be the key most recently inserted into the input object.
68+
69+
```javascript
70+
function transform( key, value ) {
71+
return value;
72+
}
73+
var obj = {};
74+
obj.a = 'beep';
75+
obj.b = 'boop';
76+
obj.c = 'beep'; // inserted after `a`
77+
78+
var opts = {
79+
'duplicates': false
80+
};
81+
var out = invertBy( obj, opts, transform );
82+
// returns { 'beep': 'c', 'boop': 'b' }
83+
```
84+
85+
The transform function is provided three arguments:
86+
87+
- **key**: object key.
88+
- **value**: object value corresponding to `key`.
89+
- **obj**: input object.
90+
91+
```javascript
92+
function transform( key, value, o ) {
93+
if ( key === 'name' ) {
94+
return value;
95+
}
96+
return o.name + ':' + value;
97+
}
98+
var obj = {
99+
'name': 'foo',
100+
'a': 'beep',
101+
'b': 'boop'
102+
};
103+
var out = invertBy( obj, transform );
104+
// returns { 'foo': 'name', 'foo:beep': 'a', 'foo:boop': 'b' }
105+
```
106+
107+
</section>
108+
109+
<!-- /.usage -->
110+
111+
<section class="notes">
112+
113+
## Notes
114+
115+
- Beware when providing objects having values which are themselves objects. This function relies on native object serialization (`#toString`) when converting transform function return values to keys.
116+
117+
```javascript
118+
function transform( key, value ) {
119+
return value;
120+
}
121+
var obj = {
122+
'a': [ 1, 2, 3 ],
123+
'b': {
124+
'c': 'd'
125+
}
126+
};
127+
128+
var out = invertBy( obj, transform );
129+
// returns { '1,2,3': 'a', '[object Object]': 'b' }
130+
```
131+
132+
- In older JavaScript engines, insertion order is not guaranteed, as object key enumeration was not specified according to the [ECMAScript specification][ecma-262-for-in] in earlier editions. In practice, however, most older engines use insertion order to sort an object's keys, thus allowing for deterministic inversion.
133+
134+
</section>
135+
136+
<!-- /.notes -->
137+
138+
<section class="examples">
139+
140+
## Examples
141+
142+
<!-- eslint no-undef: "error" -->
143+
144+
```javascript
145+
var objectKeys = require( '@stdlib/utils/keys' );
146+
var discreteUniform = require( '@stdlib/random/array/discrete-uniform' );
147+
var invertBy = require( '@stdlib/object/inverse-by' );
148+
149+
function transform( key, value ) {
150+
return value;
151+
}
152+
153+
// Create an array of random integers:
154+
var arr = discreteUniform( 1000, 0, 100, {
155+
'dtype': 'generic'
156+
});
157+
158+
// Invert the array to determine value frequency...
159+
var out = invertBy( arr, transform );
160+
var keys = objectKeys( out );
161+
162+
var i;
163+
for ( i = 0; i < keys.length; i++ ) {
164+
if ( out[ i ] ) {
165+
out[ i ] = out[ i ].length;
166+
} else {
167+
out[ i ] = 0;
168+
}
169+
}
170+
console.dir( out );
171+
```
172+
173+
</section>
174+
175+
<!-- /.examples -->
176+
177+
<!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. -->
178+
179+
<section class="related">
180+
181+
* * *
182+
183+
## See Also
184+
185+
- <span class="package-name">[`@stdlib/utils/object-inverse`][@stdlib/utils/object-inverse]</span><span class="delimiter">: </span><span class="description">invert an object, such that keys become values and values become keys.</span>
186+
187+
</section>
188+
189+
<!-- /.related -->
190+
191+
<!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
192+
193+
<section class="links">
194+
195+
[ecma-262-for-in]: https://262.ecma-international.org/5.1/#sec-12.6.4
196+
197+
<!-- <related-links> -->
198+
199+
[@stdlib/utils/object-inverse]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/utils/object-inverse
200+
201+
<!-- </related-links> -->
202+
203+
</section>
204+
205+
<!-- /.links -->
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2018 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var bench = require( '@stdlib/bench' );
24+
var randu = require( '@stdlib/random/base/randu' );
25+
var pkg = require( './../package.json' ).name;
26+
var invertBy = require( './../lib' );
27+
28+
29+
// MAIN //
30+
31+
bench( pkg, function benchmark( b ) {
32+
var obj;
33+
var out;
34+
var i;
35+
36+
function transform( key, value ) {
37+
return key + ':' + value;
38+
}
39+
obj = {
40+
'a': 'beep',
41+
'b': 'boop',
42+
'c': 'foo',
43+
'd': 'bar',
44+
'e': randu()
45+
};
46+
b.tic();
47+
for ( i = 0; i < b.iterations; i++ ) {
48+
obj.e = randu();
49+
out = invertBy( obj, transform );
50+
if ( typeof out !== 'object' ) {
51+
b.fail( 'should return an object' );
52+
}
53+
}
54+
b.toc();
55+
if ( typeof out !== 'object' ) {
56+
b.fail( 'should return an object' );
57+
}
58+
b.pass( 'benchmark finished' );
59+
b.end();
60+
});
61+
62+
bench( pkg+':duplicates=true', function benchmark( b ) {
63+
var opts;
64+
var obj;
65+
var out;
66+
var i;
67+
68+
function transform( key, value ) {
69+
return value + 'boop';
70+
}
71+
obj = {
72+
'a': 'beep',
73+
'b': 'beep',
74+
'c': 'beep',
75+
'd': 'beep',
76+
'e': randu()
77+
};
78+
opts = {
79+
'duplicates': true
80+
};
81+
b.tic();
82+
for ( i = 0; i < b.iterations; i++ ) {
83+
obj.e = randu();
84+
out = invertBy( obj, opts, transform );
85+
if ( typeof out !== 'object' ) {
86+
b.fail( 'should return an object' );
87+
}
88+
}
89+
b.toc();
90+
if ( typeof out !== 'object' ) {
91+
b.fail( 'should return an object' );
92+
}
93+
b.pass( 'benchmark finished' );
94+
b.end();
95+
});
96+
97+
bench( pkg+':duplicates=false', function benchmark( b ) {
98+
var opts;
99+
var obj;
100+
var out;
101+
var i;
102+
103+
function transform( key, value ) {
104+
return value + 'boop';
105+
}
106+
obj = {
107+
'a': 'beep',
108+
'b': 'beep',
109+
'c': 'beep',
110+
'd': 'beep',
111+
'e': randu()
112+
};
113+
opts = {
114+
'duplicates': false
115+
};
116+
b.tic();
117+
for ( i = 0; i < b.iterations; i++ ) {
118+
obj.e = randu();
119+
out = invertBy( obj, opts, transform );
120+
if ( typeof out !== 'object' ) {
121+
b.fail( 'should return an object' );
122+
}
123+
}
124+
b.toc();
125+
if ( typeof out !== 'object' ) {
126+
b.fail( 'should return an object' );
127+
}
128+
b.pass( 'benchmark finished' );
129+
b.end();
130+
});

0 commit comments

Comments
 (0)