Skip to content

Commit

Permalink
Array grouping proposal changes
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed Jun 3, 2023
1 parent a91940f commit e3a7cac
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 57 deletions.
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);
});
});

0 comments on commit e3a7cac

Please sign in to comment.