diff --git a/.gitignore b/.gitignore index 68ba43e..638d6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /.purs* /.psa* test-bundle.js +.spago diff --git a/src/Data/HashMap.js b/src/Data/HashMap.js index bfa0f1c..6052bee 100644 --- a/src/Data/HashMap.js +++ b/src/Data/HashMap.js @@ -477,6 +477,26 @@ MapNode.prototype.itraverse = function (pure, apply, f) { return m; } +MapNode.prototype.any = function (predicate) { + for (var i = 1; i < popCount(this.datamap) * 2; i = i + 2) { + var v = this.content[i]; + + if (predicate(v)) { + return true; + } + } + + i--; + + for (; i < this.content.length; i++) { + if (this.content[i].any(predicate)) { + return true; + } + } + + return false; +}; + /** @constructor */ function Collision(keys, values) { this.keys = keys; @@ -679,6 +699,15 @@ Collision.prototype.filterWithKey = function collisionFilterWithKey(f) { return new Collision(keys, values); } +Collision.prototype.any = function (predicate) { + for (var i = 0; i < this.keys.length; i++) { + if (predicate(this.values[i])) { + return true; + } + } + return false; +}; + function mask(keyHash, shift) { return 1 << ((keyHash >>> shift) & 31); } @@ -917,3 +946,9 @@ export function nubHashPurs(Nothing, Just, eq, hash) { return r; }; }; + +export function anyPurs(pred) { + return function (m) { + return m.any(pred); + }; +}; \ No newline at end of file diff --git a/src/Data/HashMap.purs b/src/Data/HashMap.purs index 7850bbe..50d0d3b 100644 --- a/src/Data/HashMap.purs +++ b/src/Data/HashMap.purs @@ -46,7 +46,9 @@ module Data.HashMap ( nubHash, - debugShow + debugShow, + + any ) where import Prelude @@ -407,3 +409,11 @@ nubHash :: forall a. Hashable a => Array a -> Array a nubHash = runFn4 nubHashPurs Nothing Just (==) hash foreign import nubHashPurs :: forall a. Fn4 (forall x. Maybe x) (forall x. x -> Maybe x) (a -> a -> Boolean) (a -> Int) (Array a -> Array a) + +foreign import anyPurs :: forall k v. (v -> Boolean) -> HashMap k v -> Boolean + +-- | Returns true if at least one HashMap element satisfies the given predicate, iterating the HashMap only as necessary and stopping as soon as the predicate yields true. +-- | +-- | Use this function instead of `Foldable.any` for more performance. +any :: forall k v. (v -> Boolean) -> HashMap k v -> Boolean +any = anyPurs diff --git a/test/Main.purs b/test/Main.purs index a51fbe2..7f24f19 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -9,7 +9,7 @@ import Prelude import Data.Array as A import Data.Array as Array import Data.Array.NonEmpty (fromNonEmpty) -import Data.Foldable (all, foldMap, foldl, foldr) +import Data.Foldable (all, foldMap, foldl, foldr, any) import Data.FoldableWithIndex (allWithIndex, foldMapWithIndex, foldlWithIndex, foldrWithIndex) import Data.HashMap (HashMap) import Data.HashMap as HM @@ -437,6 +437,26 @@ main = do Just false -> HM.lookup k mc === HM.lookup k my Nothing -> false === HM.member k my + log "any" + quickCheck' 10000 $ \(a :: Array (Tuple Int Int)) -> + let hm = HM.fromFoldable a + xs = HM.toArrayBy Tuple hm + in (not (HM.any (\_ -> true) HM.empty)) && + case A.head xs of + Nothing -> true + Just h -> case A.last xs of + Nothing -> true + Just l -> HM.any (\x -> x == snd h) hm + && HM.any (\x -> x == snd l) hm + && (not (HM.any (\_ -> false) hm)) + + log "any agrees with Foldable any" + quickCheck' 10000 $ \(a :: Array (Tuple CollidingInt Int)) (f :: Int -> Boolean) -> + let m = HM.fromArray a + f' (Tuple _ v) = f v + -- use array instance, so we still test something useful if any ever becomes a method on Foldable + in any f' a === HM.any f m + log "Done." t54 :: Boolean