From dfbd7e25440a0f85d49a0f9b7db87b243fdf4df1 Mon Sep 17 00:00:00 2001 From: karshan Date: Tue, 12 Feb 2019 17:40:29 -0800 Subject: [PATCH 1/4] Add intersection,intersectionWith --- src/Data/Map.purs | 2 +- src/Data/Map/Internal.purs | 20 ++++++++++++++++++++ test/Test/Data/Map.purs | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Data/Map.purs b/src/Data/Map.purs index e986580..89fe667 100644 --- a/src/Data/Map.purs +++ b/src/Data/Map.purs @@ -5,7 +5,7 @@ module Data.Map import Prelude -import Data.Map.Internal (Map, alter, checkValid, delete, empty, filter, filterKeys, filterWithKey, findMax, findMin, foldSubmap, fromFoldable, fromFoldableWith, fromFoldableWithIndex, insert, isEmpty, isSubmap, lookup, lookupGE, lookupGT, lookupLE, lookupLT, member, pop, showTree, singleton, size, submap, toUnfoldable, toUnfoldableUnordered, union, unionWith, unions, difference, update, values, mapMaybeWithKey, mapMaybe) +import Data.Map.Internal (Map, alter, checkValid, delete, empty, filter, filterKeys, filterWithKey, findMax, findMin, foldSubmap, fromFoldable, fromFoldableWith, fromFoldableWithIndex, insert, isEmpty, isSubmap, lookup, lookupGE, lookupGT, lookupLE, lookupLT, member, pop, showTree, singleton, size, submap, toUnfoldable, toUnfoldableUnordered, union, unionWith, unions, intersection, intersectionWith, difference, update, values, mapMaybeWithKey, mapMaybe) import Data.Set (Set) import Unsafe.Coerce (unsafeCoerce) diff --git a/src/Data/Map/Internal.purs b/src/Data/Map/Internal.purs index b3fd3d1..f8989b7 100644 --- a/src/Data/Map/Internal.purs +++ b/src/Data/Map/Internal.purs @@ -33,6 +33,8 @@ module Data.Map.Internal , union , unionWith , unions + , intersection + , intersectionWith , difference , isSubmap , size @@ -620,6 +622,24 @@ union = unionWith const unions :: forall k v f. Ord k => Foldable f => f (Map k v) -> Map k v unions = foldl union empty +-- | Compute the intersection of two maps, using the specified function +-- | to combine values for duplicate keys. +intersectionWith :: forall k a b c. Ord k => (a -> b -> c) -> Map k a -> Map k b -> Map k c +intersectionWith f m1 m2 = go (toUnfoldable m1 :: List (Tuple k a)) (toUnfoldable m2 :: List (Tuple k b)) empty + where + go Nil _ m = m + go _ Nil m = m + go as@(Cons (Tuple k1 a) ass) bs@(Cons (Tuple k2 b) bss) m = + case compare k1 k2 of + LT -> go ass bs m + EQ -> go ass bss (insert k1 (f a b) m) + GT -> go as bss m + +-- | Compute the intersection of two maps, preferring values from the first map in the case +-- | of duplicate keys. +intersection :: forall k a b. Ord k => Map k a -> Map k b -> Map k a +intersection = intersectionWith (\a b -> a) + -- | Difference of two maps. Return elements of the first map where -- | the keys do not exist in the second map. difference :: forall k v w. Ord k => Map k v -> Map k w -> Map k v diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index ca601aa..c534bf9 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -215,6 +215,23 @@ mapTests = do Just v -> Just v == v2 Nothing -> not (in1 || in2) + log "Lookup from intersection" + quickCheck $ \(TestMap m1) (TestMap m2) k -> + M.lookup (smallKey k) (M.intersection (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey Int)) == (case M.lookup k m2 of + Nothing -> Nothing + Just v -> M.lookup k m1) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", intersection: " <> show (M.intersection m1 m2)) + + log "Intersection is idempotent" + quickCheck $ \(TestMap m1) (TestMap m2) -> ((m1 :: M.Map SmallKey Int) `M.intersection` m2) == ((m1 `M.intersection` m2) `M.intersection` (m2 :: M.Map SmallKey Int)) + + log "intersectionWith" + for_ [Tuple (+) 0, Tuple (*) 1] $ \(Tuple op ident) -> + quickCheck $ \(TestMap m1) (TestMap m2) k -> + let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int + in case M.lookup k u of + Nothing -> not (M.member k m1 && M.member k m2) + Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) + log "difference" quickCheck $ \(TestMap m1) (TestMap m2) -> let d = M.difference (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey String) From b8af9ab1281e08bf5e5dc8bee9150e96a5f0cadf Mon Sep 17 00:00:00 2001 From: karshan Date: Wed, 13 Feb 2019 17:35:53 -0800 Subject: [PATCH 2/4] Make intersectionWith test more precise --- test/Test/Data/Map.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index c6c6e4f..b428fe5 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -238,7 +238,7 @@ mapTests = do let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int in case M.lookup k u of Nothing -> not (M.member k m1 && M.member k m2) - Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) + Just v -> Just v == (op <$> M.lookup k m1 <*> M.lookup k m2) log "difference" quickCheck $ \(TestMap m1) (TestMap m2) -> From c350f8d82cde30cabb8d1ba6986e7437b838b5fb Mon Sep 17 00:00:00 2001 From: karshan Date: Wed, 13 Feb 2019 18:22:43 -0800 Subject: [PATCH 3/4] Remove unused value in intersectionWith test --- test/Test/Data/Map.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index b428fe5..37d5661 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -233,7 +233,7 @@ mapTests = do quickCheck $ \(TestMap m1) (TestMap m2) -> ((m1 :: M.Map SmallKey Int) `M.intersection` m2) == ((m1 `M.intersection` m2) `M.intersection` (m2 :: M.Map SmallKey Int)) log "intersectionWith" - for_ [Tuple (+) 0, Tuple (*) 1] $ \(Tuple op ident) -> + for_ [(+), (*)] $ \op -> quickCheck $ \(TestMap m1) (TestMap m2) k -> let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int in case M.lookup k u of From ca689586fc08b3ab672aec8935c7933d5ad05ed2 Mon Sep 17 00:00:00 2001 From: karshan Date: Wed, 13 Feb 2019 18:25:37 -0800 Subject: [PATCH 4/4] Data.Map.intersection: Use const --- src/Data/Map/Internal.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Map/Internal.purs b/src/Data/Map/Internal.purs index 84270ea..1179165 100644 --- a/src/Data/Map/Internal.purs +++ b/src/Data/Map/Internal.purs @@ -646,7 +646,7 @@ intersectionWith f m1 m2 = go (toUnfoldable m1 :: List (Tuple k a)) (toUnfoldabl -- | Compute the intersection of two maps, preferring values from the first map in the case -- | of duplicate keys. intersection :: forall k a b. Ord k => Map k a -> Map k b -> Map k a -intersection = intersectionWith (\a b -> a) +intersection = intersectionWith const -- | Difference of two maps. Return elements of the first map where -- | the keys do not exist in the second map.