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

Implement ES2024 groupBy #1581

Merged
merged 1 commit into from
Aug 28, 2024
Merged

Implement ES2024 groupBy #1581

merged 1 commit into from
Aug 28, 2024

Conversation

camnwalter
Copy link
Contributor

Resolves #1545

@camnwalter camnwalter force-pushed the array-grouping branch 2 times, most recently from 767889a to 3c87078 Compare August 23, 2024 07:46
Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I saw things that could be better but this is a great submission and I'm looking forward to having it in the product!

}

// LinkedHashMap used to preserve key creation order
Map<Object, List<Object>> groups = new LinkedHashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if the spec requires that we preserve key creation order, or if we're just being nice. What do other engines do? I don't have a strong opinion but i'd generally rather stick with a regular HashMap unless the spec requires otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec says it is a list of records and appends a new group each time. I just used a map as it seemed a little easier to implement that way. Haven't researched other engines yet but I'd assume they follow it.

https://tc39.es/ecma262/#sec-add-value-to-keyed-group

Map<Object, List<Object>> groups = new LinkedHashMap<>();
final Object iterator = ScriptRuntime.callIterator(items, cx, scope);
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) {
double k = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self for the future -- I wonder if there's a way that some day we can take this code and others like it and optimize it to use integers except in the rare case where we have more than 2^31 elements in an array. (And BTW I'm not so sure that we can handle more than 2^31 elements in an array anyway...)

Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one last final thing, but I'm seeing an opportunity to make this code very clean and tight. Thanks!

for (Map.Entry<Object, List<Object>> entry : groups.entrySet()) {
Scriptable elements = cx.newArray(scope, entry.getValue().toArray());

ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last nit -- you can (and should please) replace lines 712-717 with:

obj.defineProperty(entry.getKey(), elements, ScriptableObject.PERMANENT);

This is not only less code but it's more efficient, because we're not creating a new object and messing around in a hash table to figure out what attributes to set.

Copy link
Contributor Author

@camnwalter camnwalter Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only issue with that is that the entry's key is an Object, which defineProperty doesn't allow for the first parameter. That's the main reason I did that workaround.

Edit: I just made it an if else according to if it is a symbol or not. We probably should have a dedicated function just for this, described in https://tc39.es/ecma262/#sec-topropertykey

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK -- good point -- let's merge this!

@gbrail gbrail merged commit 4e6523b into mozilla:master Aug 28, 2024
3 checks passed
* @param items
* @param callback
* @param keyCoercion
* @see <a href="https://tc39.es/ecma262/#sec-groupby"></a>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just seeing this now: the link to the spec doesn't reference a specific version of the spec, which it should 😐

@@ -232,4 +242,65 @@ static void put(Context cx, Scriptable o, int p, Object v, boolean isThrow) {
base.put(p, o, v);
}
}

/**
* Implement the ECMAScript abstract operation "GroupBy" defined in section 7.3.35 of ECMA262.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And no need to reference the section number here, as they change from version to version

@camnwalter
Copy link
Contributor Author

I'll make a separate pr addressing those couple changes if you want

@camnwalter camnwalter deleted the array-grouping branch August 28, 2024 06:02
@p-bakker
Copy link
Collaborator

Thnx, sry, should've spotted be it earlier

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ES2024 Array Grouping
3 participants