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

Missing a map() function for collections #945

Closed
sethladd opened this issue Dec 22, 2011 · 16 comments
Closed

Missing a map() function for collections #945

sethladd opened this issue Dec 22, 2011 · 16 comments

Comments

@sethladd
Copy link
Contributor

sethladd commented Dec 22, 2011

Many libraries and collection libraries have a map() function, allowing the programmer to specify a function which is applied to each element of a collection, collects the return value of the function, and returns a new collection.

In Ruby:

a = [ "a", "b", "c", "d" ]
b = a.map {|x| x + "!" }
b #=> [ "a!", "b!", "c!", "d!" ]

Proposal, for Dart:

var a = ['a', 'b', 'c'];
var b = a.map((x) => x + "!");
b # => ['a!', 'b!', 'c!']

@sethladd
Copy link
Contributor Author

sethladd commented Dec 22, 2011

Changed the title to: "Missing a map() function for collections".

@jmesserly
Copy link
Contributor

jmesserly commented Dec 22, 2011

+1, I've had to write this myself more than once already.
FWIW, we're also missing reduce(...), which JS Array has.

The main challenge is there's no way to get the right generic type signature on the return type:
    List map(Iterable source, mapper(source)) { ... }

You want to say something like:
    List<R> map<R>(Iterable<T> source, R mapper(T source)) { ... }

... but that doesn't work.

That said it'd be nice to have "map" even if it returns List<Dynamic>.


Added Area-Library label.

@jmesserly
Copy link
Contributor

jmesserly commented Dec 22, 2011

sample implementation here of "map" "reduce" and "zip" here:
http://dart.googlecode.com/svn/branches/bleeding_edge/dart/frog/utils.dart

@floitschG
Copy link
Contributor

floitschG commented Dec 22, 2011

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

@DartBot
Copy link

DartBot commented Dec 22, 2011

This comment was originally written by mattsh@google.com


I've always found both "map" and "reduce" to be somewhat unintuitive names. Let's think carefully here about the names here, there might be better choices. (This is even though I've done many mapreduces. Something like "transform" makes more sense to me.)

@jmesserly
Copy link
Contributor

jmesserly commented Dec 22, 2011

Indeed they're strange names. But perhaps they're well known by this point, that it would be worse to change? Personally I think the JS names are pretty good precedent to follow. Our other higher-order APIs on Collection are also consistent with JS Array.

FWIW, C# had an interesting take on this: use names from SQL. So "map" is "Select", "reduce" is "Aggregate", "filter" is "Where". I'm not sure if those names are really better though, or just trying to draw on a different set of preconceptions :)

@rakudrama
Copy link
Member

rakudrama commented Dec 23, 2011

Without the ability to determine the result list's type, the situation will rapidly degenerated to all lists being <Dynamic>.

You can control the result's type if you use a constructor to generate the list:

interface List<E> ... {
  // Special constructor aka map.
  List.transformedFrom(List other, E transform(element));
}

...

var capitalizedNames = new List<String>.transformedFrom(names, capitalize);
I find this a bit wordy and arcane. I would prefer to ditch map and use list comprehensions, especially since comprehensions allow combined mapping and filtering.
E.g.

var capitalizedNames = <String>[capitalize(name) for name in names];

@jmesserly
Copy link
Contributor

jmesserly commented Dec 23, 2011

Hmm. This actually might be pretty decent?
  new List<String>.from(other, (e) => e.name);

Essentially just adding an optional argument to List.from. I'm not sure how reduce() would fit in though.

but +1 to list comprehensions :)

@munificent
Copy link
Member

munificent commented Dec 23, 2011

new List<String>.from(other, (e) => e.name);

That's very nice. We could make this work with the existing List.from() just by making it an operational parameter:

new List<String>.from(other, map: (e) => e.name);

Using a constructor for this also has the advantage of making it clear that it's an eager map and not a lazy one.

@DartBot
Copy link

DartBot commented Dec 24, 2011

This comment was originally written by @seaneagan


Lazy (backed) collections are important too, for composing multiple maps and filters without creating intermediate collections, and so changes to a backing collection can be seen in other collections.

I think filter and map should be lazy, since the "from" constructors already handle the eager semantics. One can then compose lazy with eager if desired:

List<String> names = other.map<String>((e) => e.name); // issue #254 wanted here
List<String> namesSnapshot = new List.from<String>(names);

// or inline

List<String> names = new List.from<String>(other.map<String>((e) => e.name));

@DartBot
Copy link

DartBot commented Dec 27, 2011

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


To keep things simple and intuitive, I would like to +1 the original proposal of Collection#map.

If we didn't already have Collection#filter, I would take more time to consider the arguments for implementing this as a new List constructor, or by adding an argument to List.from, etc. BUT we already have Collection#filter, which I find to be very simple and intuitive. It's clear from the documentation that #filter eagerly creates and "Returns a new collection with the elements of this collection that satisfy the predicate." Similarly, I think that Dart users would find Collection#map to be clear and simple.

Honestly, I think that many Ruby/C#/underscore.js/etc developers will be expecting to have this method on collections, so I argue that we should go ahead and put it there.

Regarding the name of the method, I can see how "map" might not be the most intuitive name to some developers, although it's the most popular name for this type of function: http://en.wikipedia.org/wiki/Map_(higher-order_function). "Collect" is another good name for this function, IMHO, which is used in Ruby and Smalltalk. I also like "transform" (from C++) because it's very clear and explicit! But the huge majority of languages seem to use "map."

@rakudrama
Copy link
Member

rakudrama commented Jan 9, 2012

Since gbracha@ is currently implementing this, assigning to him.


Set owner to @gbracha.
Added Started label.

@gbracha
Copy link
Contributor

gbracha commented Jan 14, 2012

Added Fixed label.

@DartBot
Copy link

DartBot commented Jan 14, 2012

This comment was originally written by @seaneagan


Yay, fixed! Is there a CL we can look at ? or was it a spec change for issue #254 ?

@techyrajeev
Copy link

techyrajeev commented Aug 6, 2018

@sethladd is there any example of using map over linkedHashMap.
I tried below code but it gives me UnimplementedError: Map
vm.data.map((k,v) { return new MapEntry<String, Person>(k, new TabView(v));} )
data object in view model is a map with Key String and Value as Person
I referred to below documentation
https://api.dartlang.org/stable/2.0.0/dart-core/Map/map.html

Exception

I/flutter (15116): When the exception was thrown, this was the stack:
I/flutter (15116): #0      CopyOnWriteMap.map (package:built_collection/src/internal/copy_on_write_map.dart:62:5)

@mraleph
Copy link
Member

mraleph commented Aug 6, 2018

@techyrajeev please file a bug against built_collection package.

They simply did not implement it yet. See the code here: https://github.com/google/built_collection.dart/blob/master/lib/src/internal/copy_on_write_map.dart#L57-L63

(Or you can implement it and send them a PR)

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

No branches or pull requests

10 participants