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

Map should have a getter for its key/value pairs #7088

Closed
DartBot opened this issue Dec 2, 2012 · 20 comments
Closed

Map should have a getter for its key/value pairs #7088

DartBot opened this issue Dec 2, 2012 · 20 comments
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. core-2 P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@DartBot
Copy link

DartBot commented Dec 2, 2012

This issue was originally filed by @seaneagan


Map should have something like:

/// a live view of the [:Pair:]s contained in this Map
Set<Pair<K, V>> get pairs;

...and a corresponding constructor:

Map.fromPairs(Iterable<Pair<K, V>> pairs);

where Pair is just:

class Pair<K, V> {
  const Pair(this.key, this.value);
  final K key;
  final V value;
}

Examples:

print(map.pairs.mappedBy((pair) => "${pair.key} -> ${pair.value}").join(", "));

var nonNullMap = new Map.fromPairs(map.pairs.where((pair) => pair.value != null));

@sethladd
Copy link
Contributor

sethladd commented Dec 2, 2012

Removed Type-Defect label.
Added Type-Enhancement, Area-Library, Triaged labels.

@sethladd
Copy link
Contributor

sethladd commented Dec 2, 2012

From discussion https://groups.google.com/a/dartlang.org/forum/?fromgroups#!topic/misc/Y0TfPuQD4uU

Original request:

Sometimes when I have a map, I'd like to iterate over it which I can do with the forEach() method, but if I want to stop the iteration at some point, returning false won't work and neither can I break it.

I can break with a for-in loop, but it does not support multiple values:

for (String key, List<int> value in someMap) {
}

I know that I could do something like:

for (var key in someMap.keys) {
  if (someMap[key] ... )
    break;
}

However, it makes me think why can't we have a for-in with multiple values or why can't we return false in a callback for forEach()? There's also no StopIteration to throw.

Is there a reason or is it something that "just isn't there"?

@DartBot
Copy link
Author

DartBot commented Dec 3, 2012

This comment was originally written by @seaneagan


The "pairs" getter also makes sense for List, see:

https://code.google.com/p/dart/issues/detail?id=1322#c8

In JavaScript, the higher order Array method callbacks receive both the index and value, and this could provide similar functionality to Dart. Another example:

void printEvens(List list) {
  list.pairs.where((pair) {
    if(pair.key.isEven) print(pair.value);
  });
}

printEvens(['a', 'b', 'c', 'd']);
// a
// c

@DartBot
Copy link
Author

DartBot commented Aug 26, 2013

This comment was originally written by @seaneagan


Now that we have List.asMap, we can probably settle for:

list.asMap().pairs

instead of:

list.pairs

@lrhn
Copy link
Member

lrhn commented Aug 27, 2013

Issue #12766 has been merged into this issue.

1 similar comment
@lrhn
Copy link
Member

lrhn commented Aug 27, 2013

Issue #12766 has been merged into this issue.

@DartBot
Copy link
Author

DartBot commented Mar 17, 2014

This comment was originally written by @seaneagan


Tuples could be a good way to represent Map pairs. I guess the best bug for tuples is issue #10310. As seen here:

google/quiver-dart#112

tuples could be Iterables which also provide strongly typed fields for each index.

So pairs could initially just return Iterables of length 2:

Iterable<Iterable> get pairs;
Map.fromPairs(Iterable<Iterable> pairs);

and then if we manage to get tuples which extend Iterable in the future, then these APIs can be updated to use those backwards compatibly.

@DartBot
Copy link
Author

DartBot commented Mar 17, 2014

This comment was originally written by @Andersmholmgren


I agree building on some tuple solution would be the nicest

@DartBot
Copy link
Author

DartBot commented Mar 17, 2014

This comment was originally written by @Andersmholmgren


Actually maybe the cleanest solution is if map directly implements Iterable<Tup2<A,B>>

Assuming a language level syntax for tuples then you could do things like

final m = {'foo':'1', 'bar':'2'};
Final m2 = m.map((k,v) => (k, int.parse(v))
        . where((k,v) => v >= 1);

Note (at least as a first step) you could do this today without first adding support for tuples except map couldn't actually declare that it implements Iterable. That would already provide most of the value.

@DartBot
Copy link
Author

DartBot commented Nov 3, 2014

This comment was originally written by @seaneagan


Any comment from the dart team on this one? Most other mainstream languages support this:

JavaScript (ES 6): http://people.mozilla.org/~jorendorff/es6-draft.html#sec-map.prototype.entries
Java: http://stackoverflow.com/questions/46898/how-do-i-iterate-over-each-entry-in-a-map
Python: https://docs.python.org/3.3/library/stdtypes.html#dict.items
Ruby: http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-each_pair

There is obviously an overhead to creating the pair objects when iterating, but it is negligible for many use cases, and the use cases where it does matter can simply not use this API.

I understand the hesitance to have Map implement Iterable<Pair> directly, but a "composition over inheritance" approach (e.g. a "pairs" or "entries" getter) seems pretty reasonable.

@lrhn
Copy link
Member

lrhn commented Nov 6, 2014

Issue #16117 has been merged into this issue.

@lrhn
Copy link
Member

lrhn commented Nov 6, 2014

Issue #21491 has been merged into this issue.

@lrhn
Copy link
Member

lrhn commented Nov 6, 2014

Issue #16117 has been merged into this issue.

@DartBot
Copy link
Author

DartBot commented Dec 5, 2014

This comment was originally written by jirkada...@gmail.com


Angular.dart implemented KeyValue class dart-archive/angular.dart#394

Map.forEach() does not really work if I need to await something. In this example, I want to run asynchronous function doWork() for each k-v pair and I don't want to have more than one doWork executing.

import 'dart:async';

Future doWork(v) {
  var completer = new Completer();
  new Timer(new Duration(seconds: 1), () => completer.complete(v*v));
  return completer.future;
}

main() async {

  // With List, all works fine

  var l = [1,2,3,4,5,6];

  // I can either use "smart" for loop,
  
  for (var v in l) {
    var r = await doWork(v);
    print(r);
  }
  
  // or Future.forEach()

  Future.forEach(l, (v) async {
    var r = await doWork(v);
    print(r);
  });
  
  // With Map it sucks

  var m = {"I":1, "you":2, "he/she/it":3, "we": 4};
  
  // This obviously runs all iterations concurrently

  m.forEach((k,v) async {
    var r = await doWork(v);
    print("$k: $v");
  });

  // and there is no Future.forEach for Maps
}

@DartBot
Copy link
Author

DartBot commented Dec 5, 2014

This comment was originally written by greg...@gmail.com


Would it be possible to just iterate on they key and look up the value?

For example:

Future doWork(key, value) => new Future.delayed(new Duration(seconds: 1));

main() {
  Future.forEach(m.keys, (key) => doWork(key, m[key]));
}

@DartBot
Copy link
Author

DartBot commented Dec 6, 2014

This comment was originally written by jirkad...@gmail.com


@greg Sure. I came up with something similar

Future forEach(Map map, Function f) {
  return Future.forEach(map.keys, (k) async => await f(k,map[k]));
}

main() {
  forEach(m, doWork);
}

Dart is Turing complete. Every problem can be "solved".

@DartBot
Copy link
Author

DartBot commented Dec 7, 2014

This comment was originally written by greg.low...@gmail.com


My point is that Future.forEach() works fine for maps.

  Future.forEach(m.keys, (key) => doWork(key, m[key]));

  Future.forEach(m.pairs, (pair) => doWork(pair.key, pair.value));

  Future.forEachMap(m, doWork);

These options all work, and are all pretty short. But if you'd like to advocate for adding a Future.forEachMap() function, then consider opening a bug for this rather than commenting against the bug for Map.pairs.

Also worth considering, if using async/await, then Future.forEach() is not actually required at all:
  
   for (var key in m) { await doWork(key, m[key]); }

In the example from #­16, the await statement in the closure passed to Future.forEach() can be omitted, as Future.forEach() already waits if the closure returns a Future.

@DartBot
Copy link
Author

DartBot commented Jan 6, 2015

This comment was originally written by @seaneagan


Milestone-2.0 ?

@DartBot
Copy link
Author

DartBot commented Jan 6, 2015

This comment was originally written by @paulevans


Is the reason that these closure implementations are being discussed over pairs / tuples because the allocations are actually harder on the vm then the setup for the forEach closures? Or is it a previous language bias?

I'll put my hat in the ring and say I do miss the ability to iterate over pairs like I did in C#.

Though looking over the implementation of one of the maps, say https://github.com/dart-lang/bleeding_edge/blob/3c02a5f133ca8d687fe21fd4beec0081745a376d/dart/runtime/lib/linked_hash_map.dart ... you may as well just iterate over the keys and ask for the values because that is exactly what this implementation is doing anyway.

To involve Futures and the extra work + overhead they do compared to just moving over a collection seems like convenience triumphing over sanity.

@kevmoo kevmoo added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug and removed triaged labels Feb 29, 2016
@lrhn lrhn added the core-m label Aug 11, 2017
@floitschG floitschG added core-2 and removed core-m labels Aug 29, 2017
@lrhn
Copy link
Member

lrhn commented Apr 6, 2018

Maps now have an entries getter that returns an Iterable<MapEntry<K, V>>.
It was added in development version 2.0.0-dev.22.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. core-2 P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

5 participants