Repository: google/quiver-dart

Issue:
Implement a factory constructor on Multimap that copies another Multimap
Map has .from() and .of() factory constructors, why doesn't Multimap?



Fix patch:
diff --git a/AUTHORS b/AUTHORS
index fbbfdc5f..72179857 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -20,3 +20,4 @@ Alec Henninger <alechenninger@gmail.com>
 Mark Fielbig <mfielbig@gmail.com>
 Lucy Gettinger <lucyget@google.com>
 Michel Feinstein <michel@feinstein.com.br>
+Patrik Badanjak <pbadanjak@gmail.com>
diff --git a/lib/src/collection/multimap.dart b/lib/src/collection/multimap.dart
index a2d0a7f0..7435369d 100644
--- a/lib/src/collection/multimap.dart
+++ b/lib/src/collection/multimap.dart
@@ -30,6 +30,10 @@ abstract class Multimap<K, V> {
       {K Function(dynamic)? key,
       V Function(dynamic)? value}) = ListMultimap<K, V>.fromIterable;
 
+  /// Constructs a new multimap with the same values as [other].
+  factory Multimap.fromMultimap(Multimap<K, V> other) =
+      ListMultimap<K, V>.fromMultimap;
+
   /// Returns whether this multimap contains the given [value].
   bool containsValue(Object? value);
 
@@ -119,6 +123,11 @@ abstract class _BaseMultimap<K, V, C extends Iterable<V>>
     }
   }
 
+  /// Constructs a new multimap with the same values as [other].
+  _BaseMultimap.fromMultimap(Multimap<K, V> other) {
+    addAll(other);
+  }
+
   static T _id<T>(x) => x;
 
   final Map<K, C> _map = {};
@@ -240,6 +249,9 @@ class ListMultimap<K, V> extends _BaseMultimap<K, V, List<V>> {
   ListMultimap.fromIterable(super.iterable, {super.key, super.value})
       : super.fromIterable();
 
+  /// Constructs a new multimap with the same values as [other].
+  ListMultimap.fromMultimap(super.other) : super.fromMultimap();
+
   @override
   List<V> _create() => [];
   @override


Test patch:
diff --git a/test/collection/multimap_test.dart b/test/collection/multimap_test.dart
index dfcc91f2..a1e05eee 100644
--- a/test/collection/multimap_test.dart
+++ b/test/collection/multimap_test.dart
@@ -64,6 +64,50 @@ void main() {
     }, tags: ['fails-on-dartdevc']);
   });
 
+  group('Multimap.fromMultimap', () {
+    late Multimap<String, String> mmap1;
+    late Multimap<String, String> mmap2;
+    setUp(() {
+      mmap1 = Multimap()
+        ..add('k1', 'v1')
+        ..add('k1', 'v2')
+        ..add('k2', 'v3');
+
+      mmap2 = Multimap.fromMultimap(mmap1);
+    });
+
+    test('should have different memory references', () {
+      expect(false, mmap1 == mmap2);
+    });
+
+    test('should have same keys', () {
+      var map1ContainsAllMap2Keys =
+          mmap1.keys.every((key) => mmap2.containsKey(key));
+      var map2ContainsAllMap1Keys =
+          mmap2.keys.every((key) => mmap1.containsKey(key));
+
+      expect(true, map1ContainsAllMap2Keys && map2ContainsAllMap1Keys);
+    });
+
+    test('should have same values', () {
+      var map1ContainsAllMap2Values =
+          mmap1.values.every((value) => mmap2.containsValue(value));
+      var map2ContainsAllMap1Values =
+          mmap2.values.every((value) => mmap1.containsValue(value));
+
+      expect(true, map1ContainsAllMap2Values && map2ContainsAllMap1Values);
+    });
+
+    test(
+        'removing the values from the original map should leave the copy intact',
+        () {
+      mmap1.remove('k1', 'v1');
+
+      expect(false, mmap1.contains('k1', 'v1'));
+      expect(true, mmap2.contains('k1', 'v1'));
+    });
+  });
+
   group('Multimap asMap() view', () {
     late Multimap<String, String> mmap;
     late Map<String, Iterable<String>> map;