Related
How to sort a list of objects by the alphabetical order of one of its properties (Not the name but the actual value the property holds)?
You can pass a comparison function to List.sort.
someObjects.sort((a, b) => a.someProperty.compareTo(b.someProperty));
In general, you can provide a custom comparison function to List.sort.
/// Desired relation | Result
/// -------------------------------------------
/// a < b | Returns a negative value.
/// a == b | Returns 0.
/// a > b | Returns a positive value.
///
int mySortComparison(SomeClass a, SomeClass b) {
final propertyA = someProperty(a);
final propertyB = someProperty(b);
if (propertyA < propertyB) {
return -1;
} else if (propertyA > propertyB) {
return 1;
} else {
return 0;
}
}
list.sort(mySortComparison);
If you're sorting some custom class you own, you alternatively could make your class implement the Comparable interface:
class MyCustomClass implements Comparable<MyCustomClass> {
...
#override
int compareTo(MyCustomClass other) {
if (someProperty < other.someProperty) {
return -1;
} else if (someProperty > other.someProperty) {
return 1;
} else {
return 0;
}
}
}
and then you can use list.sort() directly without supplying a callback.
Note that if you're sorting by a single property that already implements the Comparable interface, implementing the comparison functions is much simpler. For example:
class MyCustomClass implements Comparable<MyCustomClass> {
...
#override
int compareTo(MyCustomClass other) =>
someProperty.compareTo(other.someProperty);
}
Reversing
If you want to reverse the sort order, you can:
Make your comparison function return a value with the opposite sign.
Alternatively just explicitly reverse the list after sorting:
list = (list..sort()).reversed.toList();
Sorting by multiple properties (a.k.a. subsorting)
There are a variety of ways to sort by multiple properties.
A general way is to perform a stable sort for each property in reverse order of importance. For example, if you want to sort names primarily by surname and then subsort within surnames by given name, then you would first sort by given names, and then perform a stable sort by surname. See below for how to perform a stable sort.
Alternatively, you could sort with a comparison function that itself checks multiple properties. For example:
class Name {
Name({String? surname, String? givenName})
: surname = surname ?? "",
givenName = givenName ?? "";
final String surname;
final String givenName;
}
int compareNames(Name name1, Name name2) {
var comparisonResult = name1.surname.compareTo(name2.surname);
if (comparisonResult != 0) {
return comparisonResult;
}
// Surnames are the same, so subsort by given name.
return name1.givenName.compareTo(name2.givenName);
}
package:collection provides an extension to chain comparison functions so that combining them is a bit more straightforward and less error-prone:
import 'package:collection/collection.dart';
int compareSurnames(Name name1, Name name2) =>
name1.surname.compareTo(name2.surname);
int compareGivenNames(Name name1, Name name2) =>
name1.givenName.compareTo(name2.givenName);
final compareNames = compareSurnames.then(compareGivenNames);
My dartbag package also provides a compareIterables function that allows comparing Lists of property values in order of importance:
import 'package:dartbag/collection.dart';
int compareNames(Name name1, Name name2) =>
compareIterables(
[name1.surname, name1.givenName],
[name2.surname, name2.givenName],
);
Okay, I want a stable sort
List.sort is not guaranteed to be a stable sort. If you need a stable sort, package:collection provides insertionSort and mergeSort implementations that are stable.
But comparing might be expensive
Suppose you have a custom comparison function that looks something like:
int compareMyCustomClass(MyCustomClass a, MyCustomClass b) {
var a0 = computeValue(a);
var b0 = computeValue(b);
return a0.compareTo(b0);
}
The sorting process might call computeValue multiple times for the same object, which is particularly wasteful if computeValue() is expensive. In such cases, a Schwartzian transform could be faster (at the expense of using more memory). This approach maps your objects to directly sortable keys, sorts those keys, and extracts the original objects. (This is how Python's sort and sorted functions work.)
Here's one possible implementation:
class _SortableKeyPair<T, K extends Comparable<Object>>
implements Comparable<_SortableKeyPair<T, K>> {
_SortableKeyPair(this.original, this.key);
final T original;
final K key;
#override
int compareTo(_SortableKeyPair<T, K> other) => key.compareTo(other.key);
}
/// Returns a sorted *copy* of [items] according to the computed sort key.
List<E> sortedWithKey<E, K extends Comparable<Object>>(
Iterable<E> items,
K Function(E) toKey,
) {
final keyPairs = [
for (var element in items) _SortableKeyPair(element, toKey(element)),
]..sort();
return [
for (var keyPair in keyPairs) keyPair.original,
];
}
void main() {
final list = <MyCustomClass>[ ... ];
final sorted = sortedWithKeys(list, computeValue);
}
My dartbag package provides such a sortWithKey function (and also a sortWithAsyncKey function if the key needs to be generated asynchronously).
If you want to sort the object "objects" by the property "name" do something like this
objects.sort((a, b) {
return a.value['name'].toString().toLowerCase().compareTo(b.value['name'].toString().toLowerCase());
});
Immutable extension sortedBy for List.
extension MyIterable<E> on Iterable<E> {
Iterable<E> sortedBy(Comparable key(E e)) =>
toList()..sort((a, b) => key(a).compareTo(key(b)));
}
And use
list.sortedBy((it) => it.name);
Here is my contribution to this good question. If someone is facing difficulty to understand how the #Nate Bosch answer is working & you want to sort your custom model class list then you can do this way.
1. You have to implement Comparable abstract class in your model class.
It has the method compareTo which you have to override.
For example, I have this StudentMarks model class which has marks property in it.
class StudentMarks implements Comparable {
int marks;
StudentMarks({
this.marks,
});
#override
int compareTo(other) {
if (this.marks == null || other == null) {
return null;
}
if (this.marks < other.marks) {
return 1;
}
if (this.marks > other.marks) {
return -1;
}
if (this.marks == other.marks) {
return 0;
}
return null;
}
}
2. Now you can call compareTo method inside the sort method.
void _sortStudents({bool reversed: false}) {
_students.sort((a, b) {
return a.compareTo(b);
});
if (reversed) {
_students = _students.reversed.toList();
}
setState(() {});
}
Refer to this link you want to know more about the Comparable class
https://api.dart.dev/stable/2.1.0/dart-core/Comparable-class.html
Its worked for me:
myList..sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
Using Comparatorfunction, sort Users by id.
Comparator<UserModel> sortById = (a, b) => a.id.compareTo(b.id);
users.sort(sortById);
Now we can sort it in reversed/descending order.
users = users.reversed.toList();
To sort it in reverse order :
list.sort((a, b) {
return b.status.toLowerCase().compareTo(a.status.toLowerCase());
});
What's more, you can use Comparable.compare for more clear, for example:
class _Person {
final int age;
final String name;
_Person({required this.age, required this.name});
}
void _test() {
final array = [
_Person(age: 10, name: 'Dean'),
_Person(age: 20, name: 'Jack'),
_Person(age: 30, name: 'Ben'),
];
// ascend with age
// Dean Jack Ben
array.sort((p1, p2) {
return Comparable.compare(p1.age, p2.age);
});
// decend with age
// Ben Jack Dean
array.sort((p1, p2) {
return Comparable.compare(p2.age, p1.age);
});
// ascend with name
// Ben Dean Jack
array.sort((p1, p2) {
return Comparable.compare(p1.name, p2.name);
});
}
Similar to #pavel-shorokhovs answer, but strongly typed:
extension IterableExtensions<T> on Iterable<T> {
Iterable<T> sortBy<TSelected extends Comparable<TSelected>>(
TSelected Function(T) selector) =>
toList()..sort((a, b) => selector(a).compareTo(selector(b)));
Iterable<T> sortByDescending<TSelected extends Comparable<TSelected>>(
TSelected Function(T) selector) =>
sortBy(selector).toList().reversed;
}
i had fpgrowth machine learning output/result with each element of list contains another list and frequency field i was to sort by frequency in descending order so i used a bit of recursion for that try it might work i know i am late but i am posting maybe someone else could benefit.
sort(List<FrequentItem> fqItems) {
int len = fqItems.length;
if(len==2){
if(fqItems[0].frequency>fqItems[1].frequency){
sortedItems.add(fqItems[0]);
sortedItems.add(fqItems[1]);
}else{
sortedItems.add(fqItems[1]);
sortedItems.add(fqItems[0]);
}
return;
}else{
FrequentItem max = fqItems[0];
int index =0;
for(int i=0;i<len-2;i++){
if(max.frequency<fqItems[i+1].frequency){
max = fqItems[i+1];
index = i+1;
}
}
sortedItems.add(max);
fqItems.removeAt(index);
sort(fqItems);
}
}
Step 1: Add compareTo method to class:
class Student {
String? name;
int? age;
Student({this.name, this.age});
int getAge() {
if (age == null) return 0;
return age!;
}
#override
int compareTo(Student other) {
var a = getAge();
var b = other.getAge();
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}
}
Step 2: Sorting your list:
By ascending:
studentList.sort((a, b) {
return a.compareTo(b);
});
By descending:
studentList.sort((a, b) {
return b.compareTo(a);
});
How to sort a list of objects by the alphabetical order of one of its properties (Not the name but the actual value the property holds)?
You can pass a comparison function to List.sort.
someObjects.sort((a, b) => a.someProperty.compareTo(b.someProperty));
In general, you can provide a custom comparison function to List.sort.
/// Desired relation | Result
/// -------------------------------------------
/// a < b | Returns a negative value.
/// a == b | Returns 0.
/// a > b | Returns a positive value.
///
int mySortComparison(SomeClass a, SomeClass b) {
final propertyA = someProperty(a);
final propertyB = someProperty(b);
if (propertyA < propertyB) {
return -1;
} else if (propertyA > propertyB) {
return 1;
} else {
return 0;
}
}
list.sort(mySortComparison);
If you're sorting some custom class you own, you alternatively could make your class implement the Comparable interface:
class MyCustomClass implements Comparable<MyCustomClass> {
...
#override
int compareTo(MyCustomClass other) {
if (someProperty < other.someProperty) {
return -1;
} else if (someProperty > other.someProperty) {
return 1;
} else {
return 0;
}
}
}
and then you can use list.sort() directly without supplying a callback.
Note that if you're sorting by a single property that already implements the Comparable interface, implementing the comparison functions is much simpler. For example:
class MyCustomClass implements Comparable<MyCustomClass> {
...
#override
int compareTo(MyCustomClass other) =>
someProperty.compareTo(other.someProperty);
}
Reversing
If you want to reverse the sort order, you can:
Make your comparison function return a value with the opposite sign.
Alternatively just explicitly reverse the list after sorting:
list = (list..sort()).reversed.toList();
Sorting by multiple properties (a.k.a. subsorting)
There are a variety of ways to sort by multiple properties.
A general way is to perform a stable sort for each property in reverse order of importance. For example, if you want to sort names primarily by surname and then subsort within surnames by given name, then you would first sort by given names, and then perform a stable sort by surname. See below for how to perform a stable sort.
Alternatively, you could sort with a comparison function that itself checks multiple properties. For example:
class Name {
Name({String? surname, String? givenName})
: surname = surname ?? "",
givenName = givenName ?? "";
final String surname;
final String givenName;
}
int compareNames(Name name1, Name name2) {
var comparisonResult = name1.surname.compareTo(name2.surname);
if (comparisonResult != 0) {
return comparisonResult;
}
// Surnames are the same, so subsort by given name.
return name1.givenName.compareTo(name2.givenName);
}
package:collection provides an extension to chain comparison functions so that combining them is a bit more straightforward and less error-prone:
import 'package:collection/collection.dart';
int compareSurnames(Name name1, Name name2) =>
name1.surname.compareTo(name2.surname);
int compareGivenNames(Name name1, Name name2) =>
name1.givenName.compareTo(name2.givenName);
final compareNames = compareSurnames.then(compareGivenNames);
My dartbag package also provides a compareIterables function that allows comparing Lists of property values in order of importance:
import 'package:dartbag/collection.dart';
int compareNames(Name name1, Name name2) =>
compareIterables(
[name1.surname, name1.givenName],
[name2.surname, name2.givenName],
);
Okay, I want a stable sort
List.sort is not guaranteed to be a stable sort. If you need a stable sort, package:collection provides insertionSort and mergeSort implementations that are stable.
But comparing might be expensive
Suppose you have a custom comparison function that looks something like:
int compareMyCustomClass(MyCustomClass a, MyCustomClass b) {
var a0 = computeValue(a);
var b0 = computeValue(b);
return a0.compareTo(b0);
}
The sorting process might call computeValue multiple times for the same object, which is particularly wasteful if computeValue() is expensive. In such cases, a Schwartzian transform could be faster (at the expense of using more memory). This approach maps your objects to directly sortable keys, sorts those keys, and extracts the original objects. (This is how Python's sort and sorted functions work.)
Here's one possible implementation:
class _SortableKeyPair<T, K extends Comparable<Object>>
implements Comparable<_SortableKeyPair<T, K>> {
_SortableKeyPair(this.original, this.key);
final T original;
final K key;
#override
int compareTo(_SortableKeyPair<T, K> other) => key.compareTo(other.key);
}
/// Returns a sorted *copy* of [items] according to the computed sort key.
List<E> sortedWithKey<E, K extends Comparable<Object>>(
Iterable<E> items,
K Function(E) toKey,
) {
final keyPairs = [
for (var element in items) _SortableKeyPair(element, toKey(element)),
]..sort();
return [
for (var keyPair in keyPairs) keyPair.original,
];
}
void main() {
final list = <MyCustomClass>[ ... ];
final sorted = sortedWithKeys(list, computeValue);
}
My dartbag package provides such a sortWithKey function (and also a sortWithAsyncKey function if the key needs to be generated asynchronously).
If you want to sort the object "objects" by the property "name" do something like this
objects.sort((a, b) {
return a.value['name'].toString().toLowerCase().compareTo(b.value['name'].toString().toLowerCase());
});
Immutable extension sortedBy for List.
extension MyIterable<E> on Iterable<E> {
Iterable<E> sortedBy(Comparable key(E e)) =>
toList()..sort((a, b) => key(a).compareTo(key(b)));
}
And use
list.sortedBy((it) => it.name);
Here is my contribution to this good question. If someone is facing difficulty to understand how the #Nate Bosch answer is working & you want to sort your custom model class list then you can do this way.
1. You have to implement Comparable abstract class in your model class.
It has the method compareTo which you have to override.
For example, I have this StudentMarks model class which has marks property in it.
class StudentMarks implements Comparable {
int marks;
StudentMarks({
this.marks,
});
#override
int compareTo(other) {
if (this.marks == null || other == null) {
return null;
}
if (this.marks < other.marks) {
return 1;
}
if (this.marks > other.marks) {
return -1;
}
if (this.marks == other.marks) {
return 0;
}
return null;
}
}
2. Now you can call compareTo method inside the sort method.
void _sortStudents({bool reversed: false}) {
_students.sort((a, b) {
return a.compareTo(b);
});
if (reversed) {
_students = _students.reversed.toList();
}
setState(() {});
}
Refer to this link you want to know more about the Comparable class
https://api.dart.dev/stable/2.1.0/dart-core/Comparable-class.html
Its worked for me:
myList..sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
Using Comparatorfunction, sort Users by id.
Comparator<UserModel> sortById = (a, b) => a.id.compareTo(b.id);
users.sort(sortById);
Now we can sort it in reversed/descending order.
users = users.reversed.toList();
To sort it in reverse order :
list.sort((a, b) {
return b.status.toLowerCase().compareTo(a.status.toLowerCase());
});
What's more, you can use Comparable.compare for more clear, for example:
class _Person {
final int age;
final String name;
_Person({required this.age, required this.name});
}
void _test() {
final array = [
_Person(age: 10, name: 'Dean'),
_Person(age: 20, name: 'Jack'),
_Person(age: 30, name: 'Ben'),
];
// ascend with age
// Dean Jack Ben
array.sort((p1, p2) {
return Comparable.compare(p1.age, p2.age);
});
// decend with age
// Ben Jack Dean
array.sort((p1, p2) {
return Comparable.compare(p2.age, p1.age);
});
// ascend with name
// Ben Dean Jack
array.sort((p1, p2) {
return Comparable.compare(p1.name, p2.name);
});
}
Similar to #pavel-shorokhovs answer, but strongly typed:
extension IterableExtensions<T> on Iterable<T> {
Iterable<T> sortBy<TSelected extends Comparable<TSelected>>(
TSelected Function(T) selector) =>
toList()..sort((a, b) => selector(a).compareTo(selector(b)));
Iterable<T> sortByDescending<TSelected extends Comparable<TSelected>>(
TSelected Function(T) selector) =>
sortBy(selector).toList().reversed;
}
i had fpgrowth machine learning output/result with each element of list contains another list and frequency field i was to sort by frequency in descending order so i used a bit of recursion for that try it might work i know i am late but i am posting maybe someone else could benefit.
sort(List<FrequentItem> fqItems) {
int len = fqItems.length;
if(len==2){
if(fqItems[0].frequency>fqItems[1].frequency){
sortedItems.add(fqItems[0]);
sortedItems.add(fqItems[1]);
}else{
sortedItems.add(fqItems[1]);
sortedItems.add(fqItems[0]);
}
return;
}else{
FrequentItem max = fqItems[0];
int index =0;
for(int i=0;i<len-2;i++){
if(max.frequency<fqItems[i+1].frequency){
max = fqItems[i+1];
index = i+1;
}
}
sortedItems.add(max);
fqItems.removeAt(index);
sort(fqItems);
}
}
Step 1: Add compareTo method to class:
class Student {
String? name;
int? age;
Student({this.name, this.age});
int getAge() {
if (age == null) return 0;
return age!;
}
#override
int compareTo(Student other) {
var a = getAge();
var b = other.getAge();
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}
}
Step 2: Sorting your list:
By ascending:
studentList.sort((a, b) {
return a.compareTo(b);
});
By descending:
studentList.sort((a, b) {
return b.compareTo(a);
});
I'm creating a function that handles objects from the database. I have two different data structures where the same property has a different name. I can't change that, so I have to handle it in JavaScript.
The objects have other differences, but that's not important to this function.
I want to use the same function for two different types of objects. Here's sample code demonstrating my problem:
interface TypeA {
itemName: string;
}
interface TypeB {
itemTitle: string;
}
function getItemName(item: TypeA | TypeB): string {
let name = '';
if (item.hasOwnProperty('itemName')) {
name = item.itemName;
} else {
name = item.itemTitle;
}
return name;
}
Of course, this code runs. But the IDE marks both the lines name = item.itemName; and name = item.itemTitle; as errors ("Property does not exist on type"), because both types do not have both properties.
So, what's the proper typescript way to do this?
You need to create a User Defined Type Guard, then you can use an if statement and get the correct typing.
function isTypeA(value: TypeA | TypeB): value is TypeA {
return value.hasOwnProperty('itemName');
}
Then you can get the typing much cleaner:
function getItemName(item: TypeA | TypeB): string {
return isTypeA(item) ? item.itemName : item.itemTitle;
}
Check it out here. Item is correctly cast to either TypeA or TypeB.
I might be a little bit late, but you could give this a try inside your function:
if ('itemName' in item) {
name = item.itemName;
} else {
name = item.itemTitle;
}
you can make a type assertion if you don't do this too often :
if (item.hasOwnProperty('itemName')) {
name = (item as TypeA).itemName;
} else {
name = (item as TypeB).itemTitle;
}
or
if (item.hasOwnProperty('itemName')) {
name = (<TypeA>item).itemName;
} else {
name = (<TypeB>item).itemTitle;
}
if you need to make this check more than once or twice, you'd better writing a type guard as #Daryl suggests.
interface TypeA {
a: string
}
interface TypeB {
b: string
}
const testFunction = (x: TypeA | TypeB): string => {
return (x as TypeA).a || (x as TypeB).b;
}
testFunction({ a: 'Hello' }); // 'Hello'
testFunction({ b: 'World' }); // 'World'
Intellij accepts this syntax:
function getItemName(item: TypeA): string;
function getItemName(item: TypeB): string;
function getItemName(item): string {
return item.hasOwnProperty('itemName') ? item.itemName : item.itemTitle;
}
the official way according to the typescript docs is this:
https://www.typescriptlang.org/docs/handbook/functions.html
I won't complicate things. If you're really sure that your object has either the one or the other property, a name = item['itemName'] || item['itemTitle'] or name = item.hasOwnProperty('itemName') ? item['itemName'] : item['itemTitle'] would be sufficient.
Note that TypeScript usually stops complaining if you access properties using the bracket notation instead of the dot notation. I would suggest adding a comment, though.
Use typeguards:
interface TypeA {
itemName: string;
}
interface TypeB {
itemTitle: string;
}
function isTypeA(val: any): val is TypeA
{
return val.hasOwnProperty('itemName');
}
function isTypeB(val: any): val is TypeB
{
return val.hasOwnProperty('itemTitle');
}
function getItemName(item: TypeA | TypeB): string
{
let name = '';
if (isTypeA(item))
{
name = item.itemName;
}
else
{
name = item.itemTitle;
}
return name;
}
I'm trying to create a recursive descent parser. So far I have all the foundation set, I just need to properly implement a few functions to enforce the grammar. I thought everything was right, it looks it, but I guess my Aop, Expr, or Term function is doing something wrong. Sometimes the input stream gets cut off and things aren't recognized. I don't see how though.
Is there any site or source that explains this more in depth, with code examples? Everything I've seen is very generic, which is fine, but I'm stuck on implementation.
NOTE: Edit April 17 2016: My functions were pretty much alright and well structured for the context of my program. The problem I was having, and didn't realize, was that at certain instances when I called getToken I "ate up" characters from the input stream. Sometimes this is fine, other times it wasn't and the input stream needed to be reset. So I simply add a small loop in cases where I needed to put back strings char by char. E.G:
if(t.getType() !=Token::EQOP)
{
//cout<<"You know it" << endl;
int size = t.getLexeme().size();
while(size>0)
{
br->putback(t.getLexeme().at(size-1));
size--;
}
return ex;
}
So with that being said, I pretty much was able to edit my program accordingly and everything worked out once I saw what was eating up the characters.
This is the grammar :
Program::= StmtList
StmtList::= Stmt | StmtList
Stmt::= PRINTKW Aop SC | INTKW VAR SC | STRKW VAR SC | Aop SC
Expr::= Expr PLUSOP Term | Expr MINUSOP Term | Term
Term::= Term STAROP Primary | Primary
Primary::= SCONST | ICONST | VAR | LPAREN Aop RPAREN
Here's the main program with all of the functions: http://pastebin.com/qMB8h8vE
The functions that I seem to be having the most trouble with is AssignmentOperator(Aop), Expression(Expr), and Term. I'll list them here.
ParseTree* Aop(istream *br)
{
ParseTree * element = Expr(br);
if(element!=0)
{
if(element->isVariable())
{
Token t= getToken(br);
if(t==Token::EQOP)
{
cout<<"No" << endl;
ParseTree * rhs = Aop(br);
if(rhs==0)
return 0;
else
{
return new AssignOp(element, rhs);
}
}
else
{
return element;
}
}
}
return 0;
}
ParseTree* Expr(istream *br)
{
ParseTree * element = Term(br);
if(element!=0)
{
Token t=getToken(br);
if(t==Token::MINUSOP || t==Token::PLUSOP)
{
if(t==Token::PLUSOP)
{
ParseTree* rhs = Expr(br);
if(rhs==0)
return 0;
else
{
return new AddOp(element, rhs);
}
}
if(t==Token::MINUSOP)
{
ParseTree* rhs = Expr(br);
if(rhs==0)
return 0;
else
{
return new SubtractOp(element, rhs); //or switch the inputs idk
}
}
}
else
{
return element;
}
}
return 0;
}
ParseTree* Term(istream *br)
{
ParseTree *element = Primary(br);
if(element!=0)
{
Token t=getToken(br);
if(t==Token::STAROP)
{
ParseTree* rhs =Term(br);
if(rhs==0)
return 0;
else
{
return new MultiplyOp(element, rhs);
}
}
else
{
return element;
}
}
return 0;
}
In order to write a recusrive descent parser, you need to convert your grammar into LL form, getting rid of left recursion. For the rules
Term::= Term STAROP Primary | Primary
you'll get something like:
Term ::= Primary Term'
Term' ::= epsilon | STAROP Primary Term'
this then turns into a function something like:
ParseTree *Term(istream *br) {
ParseTree *element = Primary(br);
while (element && peekToken(br) == Token::STAROP) {
Token t = getToken(br);
ParseTree *rhs = Primary(br);
if (!rhs) return 0;
element = new MulOp(element, rhs); }
return element;
}
Note that you're going to need a peekToken function to look ahead at the next token without consuming it. Its also possible to use getToken + ungetToken to do the same thing.
I want to define a sweet macro that transforms
{ a, b } # o
into
{ o.a, o.b }
My current attempt is
macro (#) {
case infix { { $prop:ident (,) ... } | _ $o } => {
return #{ { $prop: $o.$prop (,) ... } }
}
}
However, this give me
SyntaxError: [patterns] Ellipses level does not match in the template
I suspect I don't really understand how ... works, and may need to somehow loop over the values of $prop and build syntax objects for each and somehow concatenate them, but I'm at a loss as to how to do that.
The problem is the syntax expander thinks you're trying to expand $o.$prop instead of $prop: $o.$prop. Here's the solution:
macro (#) {
rule infix { { $prop:ident (,) ... } | $o:ident } => {
{ $($prop: $o.$prop) (,) ... }
}
}
Notice that I placed the unit of code in a $() block of its own to disambiguate the ellipse expansion.
Example: var x = { a, b } # o; becomes var x = { a: o.a, b: o.b };.