Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array grouping proposal changes #1256

Merged
merged 1 commit into from Jun 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@
- `String.prototype.toWellFormed` method
- Moved to stable ES, [May 2023 TC39 meeting](https://github.com/tc39/proposal-is-usv-string/pull/31)
- Added `es.` namespace modules, `/es/` and `/stable/` namespaces entries
- [`Array` grouping proposal](https://github.com/tc39/proposal-array-grouping):
- Because of the [web compat issue](https://github.com/tc39/proposal-array-grouping/issues/44), [moved from prototype to static methods](https://github.com/tc39/proposal-array-grouping/pull/47), [May 2023 TC39 meeting](https://github.com/babel/proposals/issues/88#issuecomment-1553350818). Added:
- `Object.groupBy`
- `Map.groupBy`
- Demoted to stage 2
- [Decorator Metadata proposal](https://github.com/tc39/proposal-decorator-metadata):
- Moved to Stage 3, [May 2023 TC39 meeting](https://github.com/babel/proposals/issues/88#issuecomment-1553366034)
- Added `Function.prototype[Symbol.metadata]` (`=== null`), [May 2023 TC39 meeting](https://github.com/babel/proposals/issues/88#issuecomment-1550313363)
Expand All @@ -23,7 +28,7 @@
- Fixed awaiting async `AsyncDisposableStack.prototype.adopt` callback, [#1258](https://github.com/zloirock/core-js/issues/1258)
- Compat data improvements:
- `Set.prototype.difference` that was missed in Bun because of [a bug](https://github.com/oven-sh/bun/issues/2309) added in 0.6.0
- `Array.prototype.{ group, groupToMap }` are disabled from Bun 0.6.2 because of [web compat issues](https://github.com/tc39/proposal-array-grouping/issues/44)
- `Array.prototype.{ group, groupToMap }` marked as no longer supported in WebKit runtimes because of the mentioned above web compat issue. For example, it's disabled from Bun 0.6.2
- Added Deno 1.34 compat data mapping
- Added Electron 26 compat data mapping
- Added Samsung Internet 22 compat data mapping
Expand Down
47 changes: 25 additions & 22 deletions README.md
Expand Up @@ -2238,28 +2238,6 @@ core-js(-pure)/full/array/from-async
```js
await Array.fromAsync((async function * (){ yield * [1, 2, 3] })(), i => i * i); // => [1, 4, 9]
```
##### [`Array` grouping](https://github.com/tc39/proposal-array-grouping)[⬆](#index)
Modules [`esnext.array.group`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.group.js), [`esnext.array.group-to-map`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.group-to-map.js).
```js
class Array {
group(callbackfn: (value: any, index: number, target: any) => key, thisArg?: any): { [key]: Array<mixed> };
groupToMap(callbackfn: (value: any, index: number, target: any) => key, thisArg?: any): Map<key, Array<mixed>>;
}
```
[*CommonJS entry points:*](#commonjs-api)
```
core-js/proposals/array-grouping-stage-3-2
core-js(-pure)/actual|full/array(/virtual)/group
core-js(-pure)/actual|full/array(/virtual)/group-to-map
```
[*Examples*](https://is.gd/3a0PbH):
```js
[1, 2, 3, 4, 5].group(it => it % 2); // => { 1: [1, 3, 5], 0: [2, 4] }

const map = [1, 2, 3, 4, 5].groupToMap(it => it % 2);
map.get(1); // => [1, 3, 5]
map.get(0); // => [2, 4]
````
##### [New `Set` methods](https://github.com/tc39/proposal-set-methods)[⬆](#index)
Modules [`esnext.set.difference.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.difference.v2.js), [`esnext.set.intersection.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.intersection.v2.js), [`esnext.set.is-disjoint-from.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.is-disjoint-from.v2.js), [`esnext.set.is-subset-of.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.is-subset-of.v2.js), [`esnext.set.is-superset-of.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.is-superset-of.v2.js), [`esnext.set.symmetric-difference.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.symmetric-difference.v2.js), [`esnext.set.union.v2`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.set.union.v2.js)
```js
Expand Down Expand Up @@ -2425,6 +2403,31 @@ core-js(-pure)/actual|full/function/metadata
```
core-js(-pure)/stage/2
```
##### [`Array` grouping](https://github.com/tc39/proposal-array-grouping)[⬆](#index)
Modules [`esnext.object.group-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.object.group-by.js), [`esnext.map.group-by`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.map.group-by.js).
```js
class Object {
groupBy(items: Iterable, callbackfn: (value: any, index: number) => key): { [key]: Array<mixed> };
}

class Map {
groupBy(items: Iterable, callbackfn: (value: any, index: number) => key): Map<key, Array<mixed>>;
}
```
[*CommonJS entry points:*](#commonjs-api)
```
core-js/proposals/array-grouping-v2
core-js(-pure)/full/map/group-by
core-js(-pure)/full/object/group-by
```
[*Examples*](https://is.gd/3a0PbH):
```js
Object.groupBy([1, 2, 3, 4, 5], it => it % 2); // => { 1: [1, 3, 5], 0: [2, 4] }

const map = Map.groupBy([1, 2, 3, 4, 5], it => it % 2);
map.get(1); // => [1, 3, 5]
map.get(0); // => [2, 4]
````
##### [`AsyncIterator` helpers](https://github.com/tc39/proposal-async-iterator-helpers)[⬆](#index)
Modules [`esnext.async-iterator.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.constructor.js), [`esnext.async-iterator.drop`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.drop.js), [`esnext.async-iterator.every`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.every.js), [`esnext.async-iterator.filter`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.filter.js), [`esnext.async-iterator.find`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.find.js), [`esnext.async-iterator.flat-map`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.flat-map.js), [`esnext.async-iterator.for-each`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.for-each.js), [`esnext.async-iterator.from`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.from.js), [`esnext.async-iterator.map`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.map.js), [`esnext.async-iterator.reduce`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.reduce.js), [`esnext.async-iterator.some`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.some.js), [`esnext.async-iterator.take`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.take.js), [`esnext.async-iterator.to-array`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.to-array.js), , [`esnext.iterator.to-async`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.iterator.to-async.js)
```js
Expand Down
6 changes: 4 additions & 2 deletions packages/core-js-compat/src/data.mjs
Expand Up @@ -1878,7 +1878,7 @@ export const data = {
// bun: '0.1.9',
// https://github.com/tc39/proposal-array-grouping/issues/44#issuecomment-1306311107
// chrome: '108',
safari: '16.4',
// safari: '16.4',
},
// TODO: Remove from `core-js@4`
'esnext.array.group-by': {
Expand All @@ -1891,7 +1891,7 @@ export const data = {
// bun: '0.1.9',
// https://github.com/tc39/proposal-array-grouping/issues/44#issuecomment-1306311107
// chrome: '108',
safari: '16.4',
// safari: '16.4',
},
'esnext.array.is-template-object': {
},
Expand Down Expand Up @@ -2112,6 +2112,8 @@ export const data = {
// TODO: Remove from `core-js@4`
'esnext.object.iterate-values': {
},
'esnext.object.group-by': {
},
// TODO: Remove this module from `core-js@4` since it's split to modules listed below
'esnext.observable': {
},
Expand Down
1 change: 1 addition & 0 deletions packages/core-js-compat/src/modules-by-versions.mjs
Expand Up @@ -206,6 +206,7 @@ export default {
'es.string.is-well-formed',
'es.string.to-well-formed',
'esnext.function.metadata',
'esnext.object.group-by',
'esnext.promise.with-resolvers',
'esnext.symbol.is-registered-symbol',
'esnext.symbol.is-well-known-symbol',
Expand Down
6 changes: 6 additions & 0 deletions packages/core-js/full/object/group-by.js
@@ -0,0 +1,6 @@
require('../../modules/es.object.create');
require('../../modules/esnext.object.group-by');

var path = require('../../internals/path');

module.exports = path.Object.groupBy;
1 change: 1 addition & 0 deletions packages/core-js/full/object/index.js
@@ -1,4 +1,5 @@
var parent = require('../../actual/object');
require('../../modules/esnext.object.group-by');
// TODO: Remove from `core-js@4`
require('../../modules/esnext.object.has-own');
require('../../modules/esnext.object.iterate-entries');
Expand Down
33 changes: 17 additions & 16 deletions packages/core-js/modules/esnext.map.group-by.js
@@ -1,29 +1,30 @@
'use strict';
var $ = require('../internals/export');
var call = require('../internals/function-call');
var uncurryThis = require('../internals/function-uncurry-this');
var isCallable = require('../internals/is-callable');
var aCallable = require('../internals/a-callable');
var requireObjectCoercible = require('../internals/require-object-coercible');
var iterate = require('../internals/iterate');
var Map = require('../internals/map-helpers').Map;
var MapHelpers = require('../internals/map-helpers');

var Map = MapHelpers.Map;
var has = MapHelpers.has;
var get = MapHelpers.get;
var set = MapHelpers.set;
var push = uncurryThis([].push);

// `Map.groupBy` method
// https://github.com/tc39/proposal-collection-methods
// https://github.com/tc39/proposal-array-grouping
$({ target: 'Map', stat: true, forced: true }, {
groupBy: function groupBy(iterable, keyDerivative) {
var C = isCallable(this) ? this : Map;
var newMap = new C();
aCallable(keyDerivative);
var has = aCallable(newMap.has);
var get = aCallable(newMap.get);
var set = aCallable(newMap.set);
iterate(iterable, function (element) {
var derivedKey = keyDerivative(element);
if (!call(has, newMap, derivedKey)) call(set, newMap, derivedKey, [element]);
else push(call(get, newMap, derivedKey), element);
groupBy: function groupBy(items, callbackfn) {
requireObjectCoercible(items);
aCallable(callbackfn);
var map = new Map();
var k = 0;
iterate(items, function (value) {
var key = callbackfn(value, k++);
if (!has(map, key)) set(map, key, [value]);
else push(get(map, key), value);
});
return newMap;
return map;
}
});
29 changes: 29 additions & 0 deletions packages/core-js/modules/esnext.object.group-by.js
@@ -0,0 +1,29 @@
'use strict';
var $ = require('../internals/export');
var getBuiltIn = require('../internals/get-built-in');
var uncurryThis = require('../internals/function-uncurry-this');
var aCallable = require('../internals/a-callable');
var requireObjectCoercible = require('../internals/require-object-coercible');
var has = require('../internals/has-own-property');
var toPropertyKey = require('../internals/to-property-key');
var iterate = require('../internals/iterate');

var create = getBuiltIn('Object', 'create');
var push = uncurryThis([].push);

// `Object.groupBy` method
// https://github.com/tc39/proposal-array-grouping
$({ target: 'Object', stat: true, forced: true }, {
groupBy: function groupBy(items, callbackfn) {
requireObjectCoercible(items);
aCallable(callbackfn);
var obj = create(null);
var k = 0;
iterate(items, function (value) {
var key = toPropertyKey(callbackfn(value, k++));
if (!has(obj, key)) obj[key] = [value];
else push(obj[key], value);
});
return obj;
}
});
3 changes: 3 additions & 0 deletions packages/core-js/proposals/array-grouping-v2.js
@@ -0,0 +1,3 @@
// https://github.com/tc39/proposal-array-grouping
require('../modules/esnext.map.group-by');
require('../modules/esnext.object.group-by');
1 change: 1 addition & 0 deletions packages/core-js/stage/2.js
@@ -1,5 +1,6 @@
var parent = require('./3');

require('../proposals/array-grouping-v2');
require('../proposals/array-is-template-object');
require('../proposals/async-explicit-resource-management');
require('../proposals/async-iterator-helpers');
Expand Down
2 changes: 1 addition & 1 deletion packages/core-js/stage/3.js
@@ -1,7 +1,6 @@
var parent = require('./4');

require('../proposals/array-from-async-stage-2');
require('../proposals/array-grouping-stage-3-2');
require('../proposals/array-buffer-transfer');
require('../proposals/decorator-metadata-v2');
require('../proposals/explicit-resource-management');
Expand All @@ -10,6 +9,7 @@ require('../proposals/json-parse-with-source');
require('../proposals/set-methods-v2');
// TODO: Obsolete versions, remove from `core-js@4`
require('../proposals/array-grouping-stage-3');
require('../proposals/array-grouping-stage-3-2');
require('../proposals/change-array-by-copy');
require('../proposals/iterator-helpers-stage-3');

Expand Down
3 changes: 3 additions & 0 deletions tests/compat/tests.js
Expand Up @@ -1690,6 +1690,9 @@ GLOBAL.tests = {
'esnext.number.from-string': function () {
return Number.fromString;
},
'esnext.object.group-by': function () {
return Object.groupBy;
},
// TODO: Remove this module from `core-js@4` since it's split to modules listed below
'esnext.observable': function () {
return Observable;
Expand Down
2 changes: 2 additions & 0 deletions tests/entries/unit.mjs
Expand Up @@ -797,6 +797,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
ok(typeof load(NS, 'object/iterate-entries')({}).next == 'function');
ok(typeof load(NS, 'object/iterate-keys')({}).next == 'function');
ok(typeof load(NS, 'object/iterate-values')({}).next == 'function');
ok(load(NS, 'object/group-by')([1, 2, 3, 4, 5], it => it % 2 === 0 ? 'even' : 'odd').odd.length === 3);
ok('from' in load(NS, 'observable'));
ok(typeof load(NS, 'reflect/define-metadata') == 'function');
ok(typeof load(NS, 'reflect/delete-metadata') == 'function');
Expand Down Expand Up @@ -896,6 +897,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
load('proposals/array-grouping');
load('proposals/array-grouping-stage-3');
load('proposals/array-grouping-stage-3-2');
load('proposals/array-grouping-v2');
load('proposals/array-includes');
load('proposals/array-is-template-object');
load('proposals/array-last');
Expand Down
16 changes: 9 additions & 7 deletions tests/unit-global/esnext.map.group-by.js
Expand Up @@ -12,13 +12,15 @@ QUnit.test('Map.groupBy', assert => {

assert.true(Map.groupBy([], it => it) instanceof Map);

assert.deepEqual(toArray(Map.groupBy([], it => it)), []);
assert.deepEqual(toArray(Map.groupBy([1, 2], it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(toArray(Map.groupBy([1, 2, 1], it => it ** 2)), [[1, [1, 1]], [4, [2]]]);
assert.deepEqual(toArray(Map.groupBy(createIterable([1, 2]), it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(toArray(groupBy([], it => it)), []);
assert.deepEqual(toArray(groupBy([1, 2], it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(toArray(groupBy([1, 2, 1], it => it ** 2)), [[1, [1, 1]], [4, [2]]]);
assert.deepEqual(toArray(groupBy(createIterable([1, 2]), it => it ** 2)), [[1, [1]], [4, [2]]]);

const element = {};
Map.groupBy([element], it => assert.same(it, element));

// assert.throws(() => groupBy([1, 2], it => it));
groupBy([element], function (it, i) {
assert.same(arguments.length, 2);
assert.same(it, element);
assert.same(i, 0);
});
});
25 changes: 25 additions & 0 deletions tests/unit-global/esnext.object.group-by.js
@@ -0,0 +1,25 @@
import { createIterable } from '../helpers/helpers';

QUnit.test('Object.groupBy', assert => {
const { groupBy, getPrototypeOf, entries } = Object;

assert.isFunction(groupBy);
assert.arity(groupBy, 2);
assert.name(groupBy, 'groupBy');
assert.looksNative(groupBy);
assert.nonEnumerable(Object, 'groupBy');

assert.same(getPrototypeOf(groupBy([], it => it)), null);

assert.deepEqual(entries(groupBy([], it => it)), []);
assert.deepEqual(entries(groupBy([1, 2], it => it ** 2)), [['1', [1]], ['4', [2]]]);
assert.deepEqual(entries(groupBy([1, 2, 1], it => it ** 2)), [['1', [1, 1]], ['4', [2]]]);
assert.deepEqual(entries(groupBy(createIterable([1, 2]), it => it ** 2)), [['1', [1]], ['4', [2]]]);

const element = {};
groupBy([element], function (it, i) {
assert.same(arguments.length, 2);
assert.same(it, element);
assert.same(i, 0);
});
});
18 changes: 10 additions & 8 deletions tests/unit-pure/esnext.map.group-by.js
Expand Up @@ -10,15 +10,17 @@ QUnit.test('Map.groupBy', assert => {
assert.arity(groupBy, 2);
assert.name(groupBy, 'groupBy');

assert.true(Map.groupBy([], it => it) instanceof Map);
assert.true(groupBy([], it => it) instanceof Map);

assert.deepEqual(from(Map.groupBy([], it => it)), []);
assert.deepEqual(from(Map.groupBy([1, 2], it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(from(Map.groupBy([1, 2, 1], it => it ** 2)), [[1, [1, 1]], [4, [2]]]);
assert.deepEqual(from(Map.groupBy(createIterable([1, 2]), it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(from(groupBy([], it => it)), []);
assert.deepEqual(from(groupBy([1, 2], it => it ** 2)), [[1, [1]], [4, [2]]]);
assert.deepEqual(from(groupBy([1, 2, 1], it => it ** 2)), [[1, [1, 1]], [4, [2]]]);
assert.deepEqual(from(groupBy(createIterable([1, 2]), it => it ** 2)), [[1, [1]], [4, [2]]]);

const element = {};
Map.groupBy([element], it => assert.same(it, element));

// assert.throws(() => groupBy([1, 2], it => it));
groupBy([element], function (it, i) {
assert.same(arguments.length, 2);
assert.same(it, element);
assert.same(i, 0);
});
});
24 changes: 24 additions & 0 deletions tests/unit-pure/esnext.object.group-by.js
@@ -0,0 +1,24 @@
import { createIterable } from '../helpers/helpers';
import groupBy from 'core-js-pure/full/object/group-by';
import getPrototypeOf from 'core-js-pure/es/object/get-prototype-of';
import entries from 'core-js-pure/es/object/entries';

QUnit.test('Object.groupBy', assert => {
assert.isFunction(groupBy);
assert.arity(groupBy, 2);
assert.name(groupBy, 'groupBy');

assert.same(getPrototypeOf(groupBy([], it => it)), null);

assert.deepEqual(entries(groupBy([], it => it)), []);
assert.deepEqual(entries(groupBy([1, 2], it => it ** 2)), [['1', [1]], ['4', [2]]]);
assert.deepEqual(entries(groupBy([1, 2, 1], it => it ** 2)), [['1', [1, 1]], ['4', [2]]]);
assert.deepEqual(entries(groupBy(createIterable([1, 2]), it => it ** 2)), [['1', [1]], ['4', [2]]]);

const element = {};
groupBy([element], function (it, i) {
assert.same(arguments.length, 2);
assert.same(it, element);
assert.same(i, 0);
});
});