Skip to content

Commit ac93365

Browse files
authoredJul 29, 2021
Merge pull request #959 from zloirock/array-grouping
2 parents 8b64ba5 + 0784656 commit ac93365

21 files changed

+246
-0
lines changed
 

‎CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
- [`Array` filtering stage 1 proposal](https://github.com/tc39/proposal-array-filtering):
55
- `Array.prototype.filterReject` replaces `Array.prototype.filterOut`
66
- `%TypedArray%.prototype.filterReject` replaces `%TypedArray%.prototype.filterOut`
7+
- Added [`Array` grouping stage 1 proposal](https://github.com/tc39/proposal-array-grouping):
8+
- `Array.prototype.groupBy`
9+
- `%TypedArray%.prototype.groupBy`
710
- Work with symbols made stricter: some missed before cases of methods that should throw an error on symbols now works as they should
811
- Handling `@@toPrimitive` in some cases of `ToPrimitive` internal logic made stricter
912
- Fixed work of `Request` with polyfilled `URLSearchParams`, [#965](https://github.com/zloirock/core-js/issues/965)

‎README.md

+22
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Promise.resolve(32).then(x => console.log(x)); // => 32
112112
- [`.of` and `.from` methods on collection constructors](#of-and-from-methods-on-collection-constructors)
113113
- [`compositeKey` and `compositeSymbol`](#compositekey-and-compositesymbol)
114114
- [`Array` filtering](#array-filtering)
115+
- [`Array` grouping](#array-grouping)
115116
- [`Array` deduplication](#array-deduplication)
116117
- [Getting last item from `Array`](#getting-last-item-from-array)
117118
- [`Number.range`](#numberrange)
@@ -2292,6 +2293,27 @@ core-js/features/typed-array/filter-reject
22922293
```js
22932294
[1, 2, 3, 4, 5].filterReject(it => it % 2); // => [2, 4]
22942295
````
2296+
##### [`Array` grouping](#https://github.com/tc39/proposal-array-grouping)[⬆](#index)
2297+
Modules [`esnext.array.group-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.group-by.js) and [`esnext.typed-array.group-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.typed-array.group-by.js).
2298+
```js
2299+
class Array {
2300+
groupBy(callbackfn: (value: any, index: number, target: any) => key, thisArg?: any): { [key]: Array<mixed> };
2301+
}
2302+
2303+
class %TypedArray% {
2304+
groupBy(callbackfn: (value: number, index: number, target: %TypedArray%) => key, thisArg?: any): { [key]: %TypedArray% };
2305+
}
2306+
```
2307+
[*CommonJS entry points:*](#commonjs-api)
2308+
```
2309+
core-js/proposals/array-grouping
2310+
core-js(-pure)/features/array(/virtual)/group-by
2311+
core-js/features/typed-array/group-by
2312+
```
2313+
[*Examples*](http://es6.zloirock.ru/#log(%5B1%2C%202%2C%203%2C%204%2C%205%5D.groupBy(it%20%3D%3E%20it%20%25%202))%3B%20%2F%2F%20%3D%3E%20%7B%201%3A%20%5B1%2C%203%2C%205%5D%2C%200%3A%20%5B2%2C%204%5D%20%7D):
2314+
```js
2315+
[1, 2, 3, 4, 5].groupBy(it => it % 2); // => { 1: [1, 3, 5], 0: [2, 4] }
2316+
````
22952317
##### [Array deduplication](https://github.com/tc39/proposal-array-unique)[⬆](#index)
22962318
Modules [`esnext.array.unique-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.unique-by.js) and [`esnext.typed-array.unique-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.typed-array.unique-by.js)
22972319
```js

‎packages/core-js-compat/src/data.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,8 @@ export const data = {
14251425
},
14261426
'esnext.array.find-last-index': {
14271427
},
1428+
'esnext.array.group-by': {
1429+
},
14281430
'esnext.array.is-template-object': {
14291431
},
14301432
'esnext.array.last-index': {
@@ -1683,6 +1685,8 @@ export const data = {
16831685
},
16841686
'esnext.typed-array.find-last-index': {
16851687
},
1688+
'esnext.typed-array.group-by': {
1689+
},
16861690
'esnext.typed-array.unique-by': {
16871691
},
16881692
'esnext.weak-map.delete-all': {

‎packages/core-js-compat/src/modules-by-versions.mjs

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ export default {
9797
],
9898
3.16: [
9999
'esnext.array.filter-reject',
100+
'esnext.array.group-by',
100101
'esnext.typed-array.filter-reject',
102+
'esnext.typed-array.group-by',
101103
],
102104
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// empty
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
require('../../modules/esnext.array.group-by');
2+
var entryUnbind = require('../../internals/entry-unbind');
3+
4+
module.exports = entryUnbind('Array', 'groupBy');

‎packages/core-js/features/array/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require('../../modules/esnext.array.filter-out');
66
require('../../modules/esnext.array.filter-reject');
77
require('../../modules/esnext.array.find-last');
88
require('../../modules/esnext.array.find-last-index');
9+
require('../../modules/esnext.array.group-by');
910
require('../../modules/esnext.array.is-template-object');
1011
require('../../modules/esnext.array.last-item');
1112
require('../../modules/esnext.array.last-index');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
require('../../../modules/esnext.array.group-by');
2+
var entryVirtual = require('../../../internals/entry-virtual');
3+
4+
module.exports = entryVirtual('Array').groupBy;

‎packages/core-js/features/array/virtual/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require('../../../modules/esnext.array.filter-out');
66
require('../../../modules/esnext.array.filter-reject');
77
require('../../../modules/esnext.array.find-last');
88
require('../../../modules/esnext.array.find-last-index');
9+
require('../../../modules/esnext.array.group-by');
910
require('../../../modules/esnext.array.unique-by');
1011

1112
module.exports = parent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var groupBy = require('../array/virtual/group-by');
2+
3+
var ArrayPrototype = Array.prototype;
4+
5+
module.exports = function (it) {
6+
var own = it.groupBy;
7+
return it === ArrayPrototype || (it instanceof Array && own === ArrayPrototype.groupBy) ? groupBy : own;
8+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('../../modules/esnext.typed-array.group-by');

‎packages/core-js/features/typed-array/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require('../../modules/esnext.typed-array.filter-out');
66
require('../../modules/esnext.typed-array.filter-reject');
77
require('../../modules/esnext.typed-array.find-last');
88
require('../../modules/esnext.typed-array.find-last-index');
9+
require('../../modules/esnext.typed-array.group-by');
910
require('../../modules/esnext.typed-array.unique-by');
1011

1112
module.exports = parent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var bind = require('../internals/function-bind-context');
2+
var IndexedObject = require('../internals/indexed-object');
3+
var toObject = require('../internals/to-object');
4+
var toLength = require('../internals/to-length');
5+
var toPropertyKey = require('../internals/to-property-key');
6+
var objectCreate = require('../internals/object-create');
7+
var arrayFromConstructorAndList = require('../internals/array-from-constructor-and-list');
8+
9+
var push = [].push;
10+
11+
module.exports = function ($this, callbackfn, that, specificConstructor) {
12+
var O = toObject($this);
13+
var self = IndexedObject(O);
14+
var boundFunction = bind(callbackfn, that, 3);
15+
var target = objectCreate(null);
16+
var length = toLength(self.length);
17+
var index = 0;
18+
var Constructor, key, value;
19+
for (;length > index; index++) {
20+
value = self[index];
21+
key = toPropertyKey(boundFunction(value, index, O));
22+
// in some IE10 builds, `hasOwnProperty` returns incorrect result on integer keys
23+
// but since it's a `null` prototype object, we can safely use `in`
24+
if (key in target) push.call(target[key], value);
25+
else target[key] = [value];
26+
}
27+
if (specificConstructor) {
28+
Constructor = specificConstructor(O);
29+
if (Constructor !== Array) {
30+
for (key in target) target[key] = arrayFromConstructorAndList(Constructor, target[key]);
31+
}
32+
} return target;
33+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
var $ = require('../internals/export');
3+
var $groupBy = require('../internals/array-group-by');
4+
var arraySpeciesConstructor = require('../internals/array-species-constructor');
5+
var addToUnscopables = require('../internals/add-to-unscopables');
6+
7+
// `Array.prototype.groupBy` method
8+
// https://github.com/tc39/proposal-array-grouping
9+
$({ target: 'Array', proto: true }, {
10+
groupBy: function groupBy(callbackfn /* , thisArg */) {
11+
var thisArg = arguments.length > 1 ? arguments[1] : undefined;
12+
return $groupBy(this, callbackfn, thisArg, arraySpeciesConstructor);
13+
}
14+
});
15+
16+
addToUnscopables('groupBy');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use strict';
2+
var ArrayBufferViewCore = require('../internals/array-buffer-view-core');
3+
var $groupBy = require('../internals/array-group-by');
4+
var typedArraySpeciesConstructor = require('../internals/typed-array-species-constructor');
5+
6+
var aTypedArray = ArrayBufferViewCore.aTypedArray;
7+
var exportTypedArrayMethod = ArrayBufferViewCore.exportTypedArrayMethod;
8+
9+
// `%TypedArray%.prototype.groupBy` method
10+
// https://github.com/tc39/proposal-array-grouping
11+
exportTypedArrayMethod('groupBy', function groupBy(callbackfn /* , thisArg */) {
12+
var thisArg = arguments.length > 1 ? arguments[1] : undefined;
13+
return $groupBy(aTypedArray(this), callbackfn, thisArg, typedArraySpeciesConstructor);
14+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// https://github.com/tc39/proposal-array-grouping
2+
require('../modules/esnext.array.group-by');
3+
require('../modules/esnext.typed-array.group-by');

‎packages/core-js/stage/1.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require('../proposals/array-filtering');
2+
require('../proposals/array-grouping');
23
require('../proposals/array-last');
34
require('../proposals/array-unique');
45
require('../proposals/collection-methods');

‎tests/commonjs.js

+10
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
7171
ok(typeof load('features/array/filter-reject') === 'function');
7272
ok(typeof load('features/array/flat') === 'function');
7373
ok(typeof load('features/array/flat-map') === 'function');
74+
ok(typeof load('features/array/group-by') === 'function');
7475
ok(typeof load('features/array/some') === 'function');
7576
ok(typeof load('features/array/every') === 'function');
7677
ok(typeof load('features/array/reduce') === 'function');
@@ -106,6 +107,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
106107
ok(typeof load('features/array/virtual/filter-reject') === 'function');
107108
ok(typeof load('features/array/virtual/flat') === 'function');
108109
ok(typeof load('features/array/virtual/flat-map') === 'function');
110+
ok(typeof load('features/array/virtual/group-by') === 'function');
109111
ok(typeof load('features/array/virtual/some') === 'function');
110112
ok(typeof load('features/array/virtual/every') === 'function');
111113
ok(typeof load('features/array/virtual/reduce') === 'function');
@@ -970,6 +972,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
970972
load('proposals/accessible-object-hasownproperty');
971973
load('proposals/array-filtering');
972974
load('proposals/array-find-from-last');
975+
load('proposals/array-grouping');
973976
load('proposals/array-is-template-object');
974977
load('proposals/array-last');
975978
load('proposals/array-unique');
@@ -1182,6 +1185,12 @@ for (PATH of ['core-js-pure', 'core-js']) {
11821185
ok(typeof instanceFilterReject([]) === 'function');
11831186
ok(instanceFilterReject([]).call([1, 2, 3], it => it % 2).length === 1);
11841187

1188+
const instanceGroupBy = load('features/instance/group-by');
1189+
ok(typeof instanceGroupBy === 'function');
1190+
ok(instanceGroupBy({}) === undefined);
1191+
ok(typeof instanceGroupBy([]) === 'function');
1192+
ok(instanceGroupBy([]).call([1, 2, 3], it => it % 2)[1].length === 2);
1193+
11851194
let instanceFindIndex = load('features/instance/find-index');
11861195
ok(typeof instanceFindIndex === 'function');
11871196
ok(instanceFindIndex({}) === undefined);
@@ -1710,6 +1719,7 @@ load('features/typed-array/fill');
17101719
load('features/typed-array/filter');
17111720
load('features/typed-array/filter-out');
17121721
load('features/typed-array/filter-reject');
1722+
load('features/typed-array/group-by');
17131723
load('features/typed-array/find');
17141724
load('features/typed-array/find-index');
17151725
load('features/typed-array/find-last');

‎tests/pure/esnext.array.group-by.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { STRICT } from '../helpers/constants';
2+
3+
import Symbol from 'core-js-pure/features/symbol';
4+
import groupBy from 'core-js-pure/features/array/group-by';
5+
import getPrototypeOf from 'core-js-pure/features/object/get-prototype-of';
6+
7+
QUnit.test('Array#groupBy', assert => {
8+
assert.isFunction(groupBy);
9+
let array = [1];
10+
const context = {};
11+
groupBy(array, function (value, key, that) {
12+
assert.same(arguments.length, 3, 'correct number of callback arguments');
13+
assert.same(value, 1, 'correct value in callback');
14+
assert.same(key, 0, 'correct index in callback');
15+
assert.same(that, array, 'correct link to array in callback');
16+
assert.same(this, context, 'correct callback context');
17+
}, context);
18+
assert.same(getPrototypeOf(groupBy([], it => it)), null, 'null proto');
19+
assert.deepEqual(groupBy([1, 2, 3], it => it % 2), { 1: [1, 3], 0: [2] }, '#1');
20+
assert.deepEqual(
21+
groupBy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], it => `i${ it % 5 }`),
22+
{ i1: [1, 6, 11], i2: [2, 7, 12], i3: [3, 8], i4: [4, 9], i0: [5, 10] },
23+
'#2',
24+
);
25+
assert.deepEqual(groupBy(Array(3), it => it), { undefined: [undefined, undefined, undefined] }, '#3');
26+
if (STRICT) {
27+
assert.throws(() => groupBy(null, () => { /* empty */ }), TypeError);
28+
assert.throws(() => groupBy(undefined, () => { /* empty */ }), TypeError);
29+
}
30+
array = [1];
31+
// eslint-disable-next-line object-shorthand -- constructor
32+
array.constructor = { [Symbol.species]: function () {
33+
return { foo: 1 };
34+
} };
35+
assert.same(groupBy(array, Boolean).true.foo, 1, '@@species');
36+
});

‎tests/tests/esnext.array.group-by.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { STRICT } from '../helpers/constants';
2+
3+
const { getPrototypeOf } = Object;
4+
5+
QUnit.test('Array#groupBy', assert => {
6+
const { groupBy } = Array.prototype;
7+
assert.isFunction(groupBy);
8+
assert.arity(groupBy, 1);
9+
assert.name(groupBy, 'groupBy');
10+
assert.looksNative(groupBy);
11+
assert.nonEnumerable(Array.prototype, 'groupBy');
12+
let array = [1];
13+
const context = {};
14+
array.groupBy(function (value, key, that) {
15+
assert.same(arguments.length, 3, 'correct number of callback arguments');
16+
assert.same(value, 1, 'correct value in callback');
17+
assert.same(key, 0, 'correct index in callback');
18+
assert.same(that, array, 'correct link to array in callback');
19+
assert.same(this, context, 'correct callback context');
20+
}, context);
21+
assert.same(getPrototypeOf([].groupBy(it => it)), null, 'null proto');
22+
assert.deepEqual([1, 2, 3].groupBy(it => it % 2), { 1: [1, 3], 0: [2] }, '#1');
23+
assert.deepEqual(
24+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].groupBy(it => `i${ it % 5 }`),
25+
{ i1: [1, 6, 11], i2: [2, 7, 12], i3: [3, 8], i4: [4, 9], i0: [5, 10] },
26+
'#2',
27+
);
28+
assert.deepEqual(Array(3).groupBy(it => it), { undefined: [undefined, undefined, undefined] }, '#3');
29+
if (STRICT) {
30+
assert.throws(() => groupBy.call(null, () => { /* empty */ }), TypeError);
31+
assert.throws(() => groupBy.call(undefined, () => { /* empty */ }), TypeError);
32+
}
33+
array = [1];
34+
// eslint-disable-next-line object-shorthand -- constructor
35+
array.constructor = { [Symbol.species]: function () {
36+
return { foo: 1 };
37+
} };
38+
assert.same(array.groupBy(Boolean).true.foo, 1, '@@species');
39+
});
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { DESCRIPTORS, GLOBAL, TYPED_ARRAYS } from '../helpers/constants';
2+
3+
const { getPrototypeOf } = Object;
4+
5+
if (DESCRIPTORS) QUnit.test('%TypedArrayPrototype%.groupBy', assert => {
6+
// we can't implement %TypedArrayPrototype% in all engines, so run all tests for each typed array constructor
7+
for (const name in TYPED_ARRAYS) {
8+
const TypedArray = GLOBAL[name];
9+
const { groupBy } = TypedArray.prototype;
10+
assert.isFunction(groupBy, `${ name }::groupBy is function`);
11+
assert.arity(groupBy, 1, `${ name }::groupBy arity is 1`);
12+
assert.name(groupBy, 'groupBy', `${ name }::groupBy name is 'groupBy'`);
13+
assert.looksNative(groupBy, `${ name }::groupBy looks native`);
14+
const array = new TypedArray([1]);
15+
const context = {};
16+
array.groupBy(function (value, key, that) {
17+
assert.same(arguments.length, 3, 'correct number of callback arguments');
18+
assert.same(value, 1, 'correct value in callback');
19+
assert.same(key, 0, 'correct index in callback');
20+
assert.same(that, array, 'correct link to array in callback');
21+
assert.same(this, context, 'correct callback context');
22+
}, context);
23+
24+
assert.same(getPrototypeOf(new TypedArray([1]).groupBy(it => it)), null, 'null proto');
25+
assert.ok(new TypedArray([1]).groupBy(it => it)[1] instanceof TypedArray, 'instance');
26+
assert.deepEqual(
27+
new TypedArray([1, 2, 3]).groupBy(it => it % 2),
28+
{ 1: new TypedArray([1, 3]), 0: new TypedArray([2]) },
29+
'#1',
30+
);
31+
assert.deepEqual(new TypedArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]).groupBy(it => `i${ it % 5 }`), {
32+
i1: new TypedArray([1, 6, 11]),
33+
i2: new TypedArray([2, 7, 12]),
34+
i3: new TypedArray([3, 8]),
35+
i4: new TypedArray([4, 9]),
36+
i0: new TypedArray([5, 10]),
37+
}, '#2');
38+
39+
assert.throws(() => groupBy.call([0], () => { /* empty */ }), "isn't generic");
40+
}
41+
});
42+

0 commit comments

Comments
 (0)
Please sign in to comment.