-
Notifications
You must be signed in to change notification settings - Fork 213
Easier loop for maps #4298
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
Comments
cc @lrhn |
Not a new request. I have some ideological oppositions to it (a map is not just a sequence of pairs!) It's definitely not an I'd then argue that if we allow that then it should be In practice, I see why you want to go through entries while naming the keys and values by what they mean in your domain, it's not like So, maybe. |
Just in case you aren't aware @HosseinYousefi , we have another proposal for using |
I figured but couldn't find an issue about it.
Iterating over a map is iterating over a sequence of pairs though.
You hit the nail on the head here. Writing +1 to having a |
Since this is in the language repository, I'll take it as a request for a What would that look like: for (var k, v in map) { ... } The That's not enough if someone wants to iterate a var map = <int, String>{...};
for (var k, v in map) ...
for (var (int k), (String v) in map) ...
int k; String v;
for (k, v in map) ... Or we could make it two "variable or declaration"s separated by comma .. then you can do No records introduced at all, no need to allocate them. The semantics would be to iterate (I'd be perfectly happy not allowing |
The types of k, v can be inferred from the statically known type parameters of |
Honestly I think two separate declarations would be better, meaning you would have to write: for (var k, var v in map) ... It's pretty common for maps to have different types for key and value, so it should be easy to express this case for (int k, String v in map) ...
// vs
for (var (int k), (String v) in map) ... Also imagine you have the following map. final Map<String, ({double latitude, double longitude})> map = {
'Chicago': (latitude: 41.878113, longitude: -87.629799),
'London': (latitude: 51.507351, longitude: -0.127758),
'Aarhus': (latitude: 56.162937, longitude: 10.203921),
}; With two separate declarations you could do the following for (String city, final (:double latitude, :double longitude) in map) ... With your proposed one declaration for both variables approach, I'm not sure how pattern matching would be supported. |
final Map<String, ({double latitude, double longitude})> map = {
'Chicago': (latitude: 41.878113, longitude: -87.629799),
'London': (latitude: 51.507351, longitude: -0.127758),
'Aarhus': (latitude: 56.162937, longitude: 10.203921),
}; For this map, you just write: for (var city, coord in map) {
print(city); // city has type "String"
print(coord.latitude); // coord has type ({double latitude, double longitude})
print(coord.longtitude);
} Both types are inferred from the static type of your map. |
Of course that would work, but what if you want the option to do a pattern match instead? Having two separate declarations easily allows for both options. // with two declarations both are fine
for (var city, var coord in map) ...
for (var city, var (:latitude, :longitude) in map) ...
// with one declaration
for (var city, coord in map) ... // fine
// how to pattern match on coord? is it supported? what is the syntax? |
var t=[(k: 1, v:'abc'), (k:2, v:'x')];
for (var (:k, :v) in t) {
print('$k $v');
} Here, in the case of an iterable, it's supported. This means it has to be supported in a map syntax, too. |
for the record, that would be destructuring, not pattern matching also, I would say that the least intrusive way of implementing this is to just introduce an extension like extension MapKVIterable<K, V> on Map<K, V> {
Iterable<(K key, V value)> get kv => entries.map((e) => (e.key, e.value));
} and maybe include that in the SDK - naming being the only thing to consider. for now, anyone here can just go ahead and copy-paste that to their projects. |
I would hope that a map-loop syntax would support it. Note that destructuring patterns currently are not supported when you have multiple variables in a single declaration. // with two declarations
// works
var city = 'Chicago';
var (:latitude, :longitude) = (latitude: 41.878113, longitude: -87.629799);
// with one declaration
// error
var city = 'Chicago', (:latitude, :longitude) = (latitude: 41.878113, longitude: -87.629799); So you would either need to allow destructuring patterns in multivariable declarations or have two separate declarations. |
var city = 'Chicago', (:latitude, :longitude) = (latitude: 41.878113, longitude: -87.629799); Any idea of why this is not supported? I thought it can cause some parsing ambiguity, or require a complicated lookahead, but I failed to discover such scenarios. |
IIRC, the grammar isn't the problem, more the human experience. var x = 42, (y, z) = p; The |
Oh, it took me a while to figure out that So, the first line is a "simultaneous assignment" 1, but the second is destructuring. Good to know! :-) Footnotes
|
It's not a simultaneous assignment. var (x, y) = (1, 2); is a destructuring declaration. A good optimizing compiler can avoid creating the object. Without the leading var (x, y) = p; is exactly the same, only the expression is Swap works, but taken literally it creates a new object before it destructures it. |
Then what is |
This comment has been minimized.
This comment has been minimized.
It's so confusing. var x=1; // x=1 - naive form
var x=(1); // x=1, might result from macro substitution
var (x)=(1); // x=1
var (x)=1; // x=1
var (x,)=(1,); // x=1
var (x)=(1,); // a totally different thing! x= (1,) Some of these forms should be flagged IMO. |
This comment has been minimized.
This comment has been minimized.
They do. You should get the unnecessary parenthesis lint triggered. Not sure if it's on by default. |
For map, this thing (suggested earlier) works with no changes to the language extension MapKVIterable<K, V> on Map<K, V> {
Iterable<(K key, V value)> get pairs => entries.map((e) => (e.key, e.value));
}
final Map<String, ({double lat, double long})> map = {
'Chicago': (lat: 41.878113, long: -87.629799),
'London': (lat: 51.507351, long: -0.127758),
'Aarhus': (lat: 56.162937, long: 10.203921),
};
main() {
for (var (city, (:lat, :long)) in map.pairs) {
print('$city $lat $long');
}
} This is the only syntax that 1) fits into a zoo of existing syntax forms 2) works |
Related: dart-lang/sdk#54965 |
I don't understand this argument from the thread^:
In any record, the roles of components are significant. E.g., we can encode coordinates |
You know, that extension could be added to the collection package, much like we have many other useful convenience methods presented, without needing to mess with the SDK any |
It won't be "added" until somebody adds it. (try submitting a patch and see how it goes?) |
Currently we can loop over maps via
map.entries
:It would be nice to have either a syntax sugar or have
Map<K, V>
implementIterable<(K, V)>
so that we can doThe text was updated successfully, but these errors were encountered: