mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 11:57:32 +09:00
Issue #2990 - implement object.GroupBy
This commit is contained in:
parent
77b07ceb5e
commit
1489a6f8b5
3 changed files with 275 additions and 145 deletions
|
|
@ -1247,6 +1247,7 @@ static const JSFunctionSpec object_static_methods[] = {
|
||||||
JS_FN("seal", obj_seal, 1, 0),
|
JS_FN("seal", obj_seal, 1, 0),
|
||||||
JS_FN("isSealed", obj_isSealed, 1, 0),
|
JS_FN("isSealed", obj_isSealed, 1, 0),
|
||||||
JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
|
JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
|
||||||
|
JS_SELF_HOSTED_FN("groupBy", "ObjectGroupBy", 2, 0),
|
||||||
JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
|
JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,228 +4,268 @@
|
||||||
|
|
||||||
// ES6 draft rev36 2015-03-17 19.1.2.1
|
// ES6 draft rev36 2015-03-17 19.1.2.1
|
||||||
function ObjectStaticAssign(target, firstSource) {
|
function ObjectStaticAssign(target, firstSource) {
|
||||||
// Steps 1-2.
|
// Steps 1-2.
|
||||||
var to = ToObject(target);
|
var to = ToObject(target);
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
if (arguments.length < 2)
|
if (arguments.length < 2) return to;
|
||||||
return to;
|
|
||||||
|
|
||||||
// Steps 4-5.
|
// Steps 4-5.
|
||||||
for (var i = 1; i < arguments.length; i++) {
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
// Step 5.a.
|
// Step 5.a.
|
||||||
var nextSource = arguments[i];
|
var nextSource = arguments[i];
|
||||||
if (nextSource === null || nextSource === undefined)
|
if (nextSource === null || nextSource === undefined) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
// Steps 5.b.i-ii.
|
// Steps 5.b.i-ii.
|
||||||
var from = ToObject(nextSource);
|
var from = ToObject(nextSource);
|
||||||
|
|
||||||
// Steps 5.b.iii-iv.
|
// Steps 5.b.iii-iv.
|
||||||
var keys = OwnPropertyKeys(from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
var keys = OwnPropertyKeys(
|
||||||
|
from,
|
||||||
|
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
|
||||||
|
);
|
||||||
|
|
||||||
// Step 5.c.
|
// Step 5.c.
|
||||||
for (var nextIndex = 0, len = keys.length; nextIndex < len; nextIndex++) {
|
for (var nextIndex = 0, len = keys.length; nextIndex < len; nextIndex++) {
|
||||||
var nextKey = keys[nextIndex];
|
var nextKey = keys[nextIndex];
|
||||||
|
|
||||||
// Steps 5.c.i-iii. We abbreviate this by calling propertyIsEnumerable
|
// Steps 5.c.i-iii. We abbreviate this by calling propertyIsEnumerable
|
||||||
// which is faster and returns false for not defined properties.
|
// which is faster and returns false for not defined properties.
|
||||||
if (callFunction(std_Object_propertyIsEnumerable, from, nextKey)) {
|
if (callFunction(std_Object_propertyIsEnumerable, from, nextKey)) {
|
||||||
// Steps 5.c.iii.1-4.
|
// Steps 5.c.iii.1-4.
|
||||||
to[nextKey] = from[nextKey];
|
to[nextKey] = from[nextKey];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 6.
|
// Step 6.
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES stage 4 proposal
|
// ES stage 4 proposal
|
||||||
function ObjectGetOwnPropertyDescriptors(O) {
|
function ObjectGetOwnPropertyDescriptors(O) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var obj = ToObject(O);
|
var obj = ToObject(O);
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
var keys = OwnPropertyKeys(obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
var keys = OwnPropertyKeys(
|
||||||
|
obj,
|
||||||
|
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
|
||||||
|
);
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
var descriptors = {};
|
var descriptors = {};
|
||||||
|
|
||||||
// Step 4.
|
// Step 4.
|
||||||
for (var index = 0, len = keys.length; index < len; index++) {
|
for (var index = 0, len = keys.length; index < len; index++) {
|
||||||
var key = keys[index];
|
var key = keys[index];
|
||||||
|
|
||||||
// Steps 4.a-b.
|
// Steps 4.a-b.
|
||||||
var desc = std_Object_getOwnPropertyDescriptor(obj, key);
|
var desc = std_Object_getOwnPropertyDescriptor(obj, key);
|
||||||
|
|
||||||
// Step 4.c.
|
// Step 4.c.
|
||||||
if (typeof desc !== "undefined")
|
if (typeof desc !== "undefined")
|
||||||
_DefineDataProperty(descriptors, key, desc);
|
_DefineDataProperty(descriptors, key, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.9. */
|
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.9. */
|
||||||
function ObjectGetPrototypeOf(obj) {
|
function ObjectGetPrototypeOf(obj) {
|
||||||
return std_Reflect_getPrototypeOf(ToObject(obj));
|
return std_Reflect_getPrototypeOf(ToObject(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.11. */
|
/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.11. */
|
||||||
function ObjectIsExtensible(obj) {
|
function ObjectIsExtensible(obj) {
|
||||||
return IsObject(obj) && std_Reflect_isExtensible(obj);
|
return IsObject(obj) && std_Reflect_isExtensible(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
|
/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
|
||||||
function Object_toLocaleString() {
|
function Object_toLocaleString() {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var O = this;
|
var O = this;
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
return callContentFunction(O.toString, O);
|
return callContentFunction(O.toString, O);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7
|
// ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7
|
||||||
function Object_valueOf() {
|
function Object_valueOf() {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
return ToObject(this);
|
return ToObject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES7 draft (2016 March 8) B.2.2.3
|
// ES7 draft (2016 March 8) B.2.2.3
|
||||||
function ObjectDefineSetter(name, setter) {
|
function ObjectDefineSetter(name, setter) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var object = ToObject(this);
|
var object = ToObject(this);
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
if (!IsCallable(setter))
|
if (!IsCallable(setter)) ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
|
||||||
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
|
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
var desc = {
|
var desc = {
|
||||||
__proto__: null,
|
__proto__: null,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
set: setter
|
set: setter,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 4.
|
// Step 4.
|
||||||
var key = ToPropertyKey(name);
|
var key = ToPropertyKey(name);
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
std_Object_defineProperty(object, key, desc);
|
std_Object_defineProperty(object, key, desc);
|
||||||
|
|
||||||
// Step 6. (implicit)
|
// Step 6. (implicit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES7 draft (2016 March 8) B.2.2.2
|
// ES7 draft (2016 March 8) B.2.2.2
|
||||||
function ObjectDefineGetter(name, getter) {
|
function ObjectDefineGetter(name, getter) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var object = ToObject(this);
|
var object = ToObject(this);
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
if (!IsCallable(getter))
|
if (!IsCallable(getter)) ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter");
|
||||||
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter");
|
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
var desc = {
|
var desc = {
|
||||||
__proto__: null,
|
__proto__: null,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: getter
|
get: getter,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 4.
|
// Step 4.
|
||||||
var key = ToPropertyKey(name);
|
var key = ToPropertyKey(name);
|
||||||
|
|
||||||
// Step 5.
|
// Step 5.
|
||||||
std_Object_defineProperty(object, key, desc);
|
std_Object_defineProperty(object, key, desc);
|
||||||
|
|
||||||
// Step 6. (implicit)
|
// Step 6. (implicit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES7 draft (2016 March 8) B.2.2.5
|
// ES7 draft (2016 March 8) B.2.2.5
|
||||||
function ObjectLookupSetter(name) {
|
function ObjectLookupSetter(name) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var object = ToObject(this);
|
var object = ToObject(this);
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
var key = ToPropertyKey(name)
|
var key = ToPropertyKey(name);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Step 3.a.
|
// Step 3.a.
|
||||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||||
|
|
||||||
// Step 3.b.
|
// Step 3.b.
|
||||||
if (desc) {
|
if (desc) {
|
||||||
// Step.b.i.
|
// Step.b.i.
|
||||||
if (callFunction(std_Object_hasOwnProperty, desc, "set"))
|
if (callFunction(std_Object_hasOwnProperty, desc, "set")) return desc.set;
|
||||||
return desc.set;
|
|
||||||
|
|
||||||
// Step.b.ii.
|
// Step.b.ii.
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.c.
|
// Step 3.c.
|
||||||
object = std_Reflect_getPrototypeOf(object);
|
object = std_Reflect_getPrototypeOf(object);
|
||||||
} while (object !== null);
|
} while (object !== null);
|
||||||
|
|
||||||
// Step 3.d. (implicit)
|
// Step 3.d. (implicit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES7 draft (2016 March 8) B.2.2.4
|
// ES7 draft (2016 March 8) B.2.2.4
|
||||||
function ObjectLookupGetter(name) {
|
function ObjectLookupGetter(name) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var object = ToObject(this);
|
var object = ToObject(this);
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
var key = ToPropertyKey(name)
|
var key = ToPropertyKey(name);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Step 3.a.
|
// Step 3.a.
|
||||||
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
var desc = std_Object_getOwnPropertyDescriptor(object, key);
|
||||||
|
|
||||||
// Step 3.b.
|
// Step 3.b.
|
||||||
if (desc) {
|
if (desc) {
|
||||||
// Step.b.i.
|
// Step.b.i.
|
||||||
if (callFunction(std_Object_hasOwnProperty, desc, "get"))
|
if (callFunction(std_Object_hasOwnProperty, desc, "get")) return desc.get;
|
||||||
return desc.get;
|
|
||||||
|
|
||||||
// Step.b.ii.
|
// Step.b.ii.
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.c.
|
// Step 3.c.
|
||||||
object = std_Reflect_getPrototypeOf(object);
|
object = std_Reflect_getPrototypeOf(object);
|
||||||
} while (object !== null);
|
} while (object !== null);
|
||||||
|
|
||||||
// Step 3.d. (implicit)
|
// Step 3.d. (implicit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stage 4 draft 2020-09-06 https://tc39.github.io/proposal-object-from-entries/
|
// Stage 4 draft 2020-09-06 https://tc39.github.io/proposal-object-from-entries/
|
||||||
// Object.fromEntries (iterable)
|
// Object.fromEntries (iterable)
|
||||||
function ObjectFromEntries(iter) {
|
function ObjectFromEntries(iter) {
|
||||||
// We omit the usual step number comments here because they don't help.
|
// We omit the usual step number comments here because they don't help.
|
||||||
// This implementation inlines AddEntriesFromIterator and
|
// This implementation inlines AddEntriesFromIterator and
|
||||||
// CreateDataPropertyOnObject, so it looks more like the polyfill
|
// CreateDataPropertyOnObject, so it looks more like the polyfill
|
||||||
// than the step-by-step spec algorithm.
|
// than the step-by-step spec algorithm.
|
||||||
const obj = {};
|
const obj = {};
|
||||||
|
|
||||||
for (const pair of allowContentIter(iter)) {
|
for (const pair of allowContentIter(iter)) {
|
||||||
if (!IsObject(pair))
|
if (!IsObject(pair))
|
||||||
ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
|
ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
|
||||||
_DefineDataProperty(obj, pair[0], pair[1]);
|
_DefineDataProperty(obj, pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ES2024
|
||||||
|
// Object.groupBy ( items, callbackfn )
|
||||||
|
function ObjectGroupBy(items, callbackfn) {
|
||||||
|
// Step 1.
|
||||||
|
RequireObjectCoercible(items);
|
||||||
|
|
||||||
|
// Step 2.
|
||||||
|
if (!IsCallable(callbackfn))
|
||||||
|
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
var groups = std_Object_create(null);
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
var k = 0;
|
||||||
|
|
||||||
|
// Steps 5-8.
|
||||||
|
for (var value of allowContentIter(items)) {
|
||||||
|
// Step 6.a.
|
||||||
|
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||||
|
|
||||||
|
// Step 6.b.
|
||||||
|
key = ToPropertyKey(key);
|
||||||
|
|
||||||
|
// Steps 6.c-d.
|
||||||
|
var elements = groups[key];
|
||||||
|
if (elements === undefined) {
|
||||||
|
_DefineDataProperty(groups, key, [value]);
|
||||||
|
} else {
|
||||||
|
callFunction(std_Array_push, elements, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
// Step 6.e.
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9.
|
||||||
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proposal https://github.com/tc39/proposal-accessible-object-hasownproperty
|
// Proposal https://github.com/tc39/proposal-accessible-object-hasownproperty
|
||||||
// Object.hasOwn (Object, Property)
|
// Object.hasOwn (Object, Property)
|
||||||
function ObjectHasOwn(O, P) {
|
function ObjectHasOwn(O, P) {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
var obj = ToObject(O);
|
var obj = ToObject(O);
|
||||||
// Step 2-3.
|
// Step 2-3.
|
||||||
return callFunction(std_Object_hasOwnProperty, obj, P);
|
return callFunction(std_Object_hasOwnProperty, obj, P);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
89
js/src/tests/ecma_6/Object/groupBy.js
Normal file
89
js/src/tests/ecma_6/Object/groupBy.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/licenses/publicdomain/
|
||||||
|
*/
|
||||||
|
|
||||||
|
assertEq(typeof Object.groupBy, "function");
|
||||||
|
assertEq(Object.groupBy.length, 2);
|
||||||
|
|
||||||
|
let desc = Object.getOwnPropertyDescriptor(Object, "groupBy");
|
||||||
|
assertEq(desc.enumerable, false);
|
||||||
|
assertEq(desc.writable, true);
|
||||||
|
assertEq(desc.configurable, true);
|
||||||
|
|
||||||
|
{
|
||||||
|
let values = [1, 2, 3, 4, 5];
|
||||||
|
let calls = [];
|
||||||
|
|
||||||
|
let grouped = Object.groupBy(values, function (value, index) {
|
||||||
|
"use strict";
|
||||||
|
assertEq(this, undefined);
|
||||||
|
calls.push([value, index]);
|
||||||
|
return value % 2 ? "odd" : "even";
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEq(Object.getPrototypeOf(grouped), null);
|
||||||
|
assertDeepEq(Object.keys(grouped), ["odd", "even"]);
|
||||||
|
assertDeepEq(grouped.odd, [1, 3, 5]);
|
||||||
|
assertDeepEq(grouped.even, [2, 4]);
|
||||||
|
assertDeepEq(calls, [[1, 0], [2, 1], [3, 2], [4, 3], [5, 4]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let sym = Symbol("key");
|
||||||
|
let grouped = Object.groupBy([10, 11], value => value === 10 ? sym : 0);
|
||||||
|
|
||||||
|
assertDeepEq(Object.keys(grouped), ["0"]);
|
||||||
|
assertDeepEq(grouped[0], [11]);
|
||||||
|
|
||||||
|
let symbols = Object.getOwnPropertySymbols(grouped);
|
||||||
|
assertEq(symbols.length, 1);
|
||||||
|
assertEq(symbols[0], sym);
|
||||||
|
assertDeepEq(grouped[sym], [10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let grouped = Object.groupBy([1, 2], () => "__proto__");
|
||||||
|
assertEq(Object.getPrototypeOf(grouped), null);
|
||||||
|
assertDeepEq(grouped["__proto__"], [1, 2]);
|
||||||
|
|
||||||
|
let protoKeyDesc = Object.getOwnPropertyDescriptor(grouped, "__proto__");
|
||||||
|
assertEq(protoKeyDesc.enumerable, true);
|
||||||
|
assertEq(protoKeyDesc.writable, true);
|
||||||
|
assertEq(protoKeyDesc.configurable, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let closed = false;
|
||||||
|
let iterable = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
let i = 0;
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
i++;
|
||||||
|
if (i <= 3)
|
||||||
|
return { value: i, done: false };
|
||||||
|
return { done: true };
|
||||||
|
},
|
||||||
|
return() {
|
||||||
|
closed = true;
|
||||||
|
return { done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assertThrowsValue(() => Object.groupBy(iterable, value => {
|
||||||
|
if (value === 2)
|
||||||
|
throw 42;
|
||||||
|
return "x";
|
||||||
|
}), 42);
|
||||||
|
assertEq(closed, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThrowsInstanceOf(() => Object.groupBy([], null), TypeError);
|
||||||
|
assertThrowsInstanceOf(() => Object.groupBy(null, null), TypeError);
|
||||||
|
assertThrowsInstanceOf(() => Object.groupBy({}, x => x), TypeError);
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue