[server] Add ordered map
It works similar to a map, but additionally keeps track of the order of its keys.
This commit is contained in:
parent
4d06e48a82
commit
964b13739a
1 changed files with 275 additions and 0 deletions
275
src/Forest/OrderedMap.hs
Normal file
275
src/Forest/OrderedMap.hs
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
module Forest.OrderedMap
|
||||
( OrderedMap
|
||||
-- * Construction
|
||||
, empty
|
||||
, singleton
|
||||
, fromSet
|
||||
-- ** From lists
|
||||
, fromList
|
||||
, fromListWith
|
||||
, fromListWithKey
|
||||
-- ** From maps
|
||||
, fromMap
|
||||
, fromMapWithOrder
|
||||
-- * Insertion
|
||||
, append
|
||||
, appendWith
|
||||
, appendWithKey
|
||||
, appendLookupWithKey
|
||||
, prepend
|
||||
, prependWith
|
||||
, prependWithKey
|
||||
, prependLookupWithKey
|
||||
-- * Deletion/Update
|
||||
, delete
|
||||
, adjust
|
||||
, adjustWithKey
|
||||
, update
|
||||
, updateWithKey
|
||||
-- * Query
|
||||
-- ** Lookup
|
||||
, Forest.OrderedMap.lookup
|
||||
, (!?)
|
||||
, (!)
|
||||
, findWithDefault
|
||||
, member
|
||||
, notMember
|
||||
, lookupLT
|
||||
, lookupGT
|
||||
, lookupLE
|
||||
, lookupGE
|
||||
-- ** Size
|
||||
, Forest.OrderedMap.null
|
||||
, size
|
||||
-- * Traversal
|
||||
-- ** Map
|
||||
, Forest.OrderedMap.map
|
||||
, mapWithKey
|
||||
-- * Conversion
|
||||
, elems
|
||||
, keys
|
||||
, assocs
|
||||
, keysSet
|
||||
-- ** Lists
|
||||
, toList
|
||||
-- ** Maps
|
||||
, toMap
|
||||
-- * Filter
|
||||
, Forest.OrderedMap.filter
|
||||
) where
|
||||
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Set as Set
|
||||
|
||||
data OrderedMap k a = OrderedMap
|
||||
{ omMap :: Map.Map k a
|
||||
, omOrder :: [k]
|
||||
}
|
||||
|
||||
instance (Ord k, Show k, Show a) => Show (OrderedMap k a) where
|
||||
show m = "fromList " ++ show (toList m)
|
||||
|
||||
-- Invariants of this data type:
|
||||
--
|
||||
-- 1. The 'omOrder' list contains each key from 'omMap' exactly once.
|
||||
-- 2. The 'omOrder' list contains no other values.
|
||||
--
|
||||
-- All functions operating on an 'OrderedMap' may assume these invariants. All
|
||||
-- modifications must preserve these invariants.
|
||||
|
||||
-- | Like 'Map.empty'.
|
||||
empty :: OrderedMap k a
|
||||
empty = OrderedMap
|
||||
{ omMap = Map.empty
|
||||
, omOrder = []
|
||||
}
|
||||
|
||||
-- | Like 'Map.singleton'.
|
||||
singleton :: k -> a -> OrderedMap k a
|
||||
singleton k a = OrderedMap
|
||||
{ omMap = Map.singleton k a
|
||||
, omOrder = [k]
|
||||
}
|
||||
|
||||
-- | Like 'Map.fromSet'. Uses 'Set.toAscList' as the order.
|
||||
fromSet :: (k -> a) -> Set.Set k -> OrderedMap k a
|
||||
fromSet f kSet = OrderedMap
|
||||
{ omMap = Map.fromSet f kSet
|
||||
, omOrder = Set.toAscList kSet
|
||||
}
|
||||
|
||||
fromMap :: Map.Map k a -> OrderedMap k a
|
||||
fromMap m = OrderedMap
|
||||
{ omMap = m
|
||||
, omOrder = Map.keys m
|
||||
}
|
||||
|
||||
-- | Create a new 'OrderedMap' from a 'Map.Map' using a list of keys to specify
|
||||
-- the order. If the list of keys contains a key multiple times, only the last
|
||||
-- occurrence is counted. The keys missing from the list of keys are appended at
|
||||
-- the end in ascending order.
|
||||
fromMapWithOrder :: Ord k => Map.Map k a -> [k] -> OrderedMap k a
|
||||
fromMapWithOrder m l =
|
||||
let kSet = Map.keysSet m
|
||||
onlyExistingKeys = L.filter (`Set.member` kSet) l
|
||||
deduplicatedKeys = fst $ keepLastInstances onlyExistingKeys
|
||||
missingKeysSet = kSet Set.\\ Set.fromList deduplicatedKeys
|
||||
order = if Set.null missingKeysSet
|
||||
then deduplicatedKeys -- For the extra performance :P
|
||||
else deduplicatedKeys ++ Set.toAscList missingKeysSet
|
||||
in OrderedMap {omMap = m, omOrder = order}
|
||||
|
||||
keepLastInstances :: Ord a => [a] -> ([a], Set.Set a)
|
||||
keepLastInstances [] = ([], Set.empty)
|
||||
keepLastInstances (x:xs)
|
||||
| x `Set.member` subset = (sublist, subset)
|
||||
| otherwise = (x : sublist, Set.insert x subset)
|
||||
where
|
||||
(sublist, subset) = keepLastInstances xs
|
||||
|
||||
-- | Like 'Map.fromList'. Uses the last occurrence of each key as the order.
|
||||
fromList :: Ord k => [(k, a)] -> OrderedMap k a
|
||||
-- This function could've been implemented as @fromListWith const@, but that
|
||||
-- might lead to a performance penalty. I don't know though ¯\_(ツ)_/¯
|
||||
fromList pairs = OrderedMap
|
||||
{ omMap = Map.fromList pairs
|
||||
, omOrder = fst $ keepLastInstances $ L.map fst pairs
|
||||
}
|
||||
|
||||
-- | Like 'Map.fromListWith'. Uses the last occurrence of each key as the order.
|
||||
fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> OrderedMap k a
|
||||
fromListWith f = fromListWithKey $ const f
|
||||
|
||||
-- | Like 'Map.fromListWithKey'. Uses the last occurrence of each key as the
|
||||
-- order.
|
||||
fromListWithKey :: Ord k => (k -> a -> a -> a) -> [(k, a)] -> OrderedMap k a
|
||||
fromListWithKey f pairs = OrderedMap
|
||||
{ omMap = Map.fromListWithKey f pairs
|
||||
, omOrder = fst $ keepLastInstances $ L.map fst pairs
|
||||
}
|
||||
|
||||
append :: Ord k => k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
append = appendWith const
|
||||
|
||||
appendWith :: Ord k => (a -> a -> a) -> k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
appendWith f = appendWithKey $ const f
|
||||
|
||||
appendWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
appendWithKey f k a = snd . appendLookupWithKey f k a
|
||||
|
||||
appendLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> OrderedMap k a -> (Maybe a, OrderedMap k a)
|
||||
appendLookupWithKey f k a m =
|
||||
let (maybePrevA, newMap) = Map.insertLookupWithKey f k a $ omMap m
|
||||
newOrder = case maybePrevA of
|
||||
Nothing -> omOrder m ++ [k]
|
||||
Just _ -> omOrder m
|
||||
in (maybePrevA, OrderedMap {omMap = newMap , omOrder = newOrder})
|
||||
|
||||
prepend :: Ord k => k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
prepend = prependWith const
|
||||
|
||||
prependWith :: Ord k => (a -> a -> a) -> k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
prependWith f = prependWithKey $ const f
|
||||
|
||||
prependWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> OrderedMap k a -> OrderedMap k a
|
||||
prependWithKey f k a = snd . prependLookupWithKey f k a
|
||||
|
||||
prependLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> OrderedMap k a -> (Maybe a, OrderedMap k a)
|
||||
prependLookupWithKey f k a m =
|
||||
let (maybePrevA, newMap) = Map.insertLookupWithKey f k a $ omMap m
|
||||
newOrder = case maybePrevA of
|
||||
Nothing -> k : omOrder m
|
||||
Just _ -> omOrder m
|
||||
in (maybePrevA, OrderedMap {omMap = newMap , omOrder = newOrder})
|
||||
|
||||
delete :: Ord k => k -> OrderedMap k a -> OrderedMap k a
|
||||
delete k m = m
|
||||
{ omMap = Map.delete k $ omMap m
|
||||
, omOrder = L.delete k $ omOrder m
|
||||
}
|
||||
|
||||
adjust :: Ord k => (a -> a) -> k -> OrderedMap k a -> OrderedMap k a
|
||||
adjust f = adjustWithKey $ const f
|
||||
|
||||
adjustWithKey :: Ord k => (k -> a -> a) -> k -> OrderedMap k a -> OrderedMap k a
|
||||
adjustWithKey f k m = m {omMap = Map.adjustWithKey f k $ omMap m}
|
||||
|
||||
update :: Ord k => (a -> Maybe a) -> k -> OrderedMap k a -> OrderedMap k a
|
||||
update f = updateWithKey $ const f
|
||||
|
||||
updateWithKey :: Ord k => (k -> a -> Maybe a) -> k -> OrderedMap k a -> OrderedMap k a
|
||||
updateWithKey f k m =
|
||||
let newMap = Map.updateWithKey f k $ omMap m
|
||||
newOrder = case newMap Map.!? k of
|
||||
Nothing -> L.delete k $ omOrder m
|
||||
Just _ -> omOrder m
|
||||
in OrderedMap {omMap = newMap, omOrder = newOrder}
|
||||
|
||||
lookup :: Ord k => k -> OrderedMap k a -> Maybe a
|
||||
lookup k = Map.lookup k . omMap
|
||||
|
||||
infixl 9 !?
|
||||
(!?) :: Ord k => OrderedMap k a -> k -> Maybe a
|
||||
m !? k = omMap m Map.!? k
|
||||
|
||||
infixl 9 !
|
||||
(!) :: Ord k => OrderedMap k a -> k -> a
|
||||
m ! k = omMap m Map.! k
|
||||
|
||||
findWithDefault :: Ord k => a -> k -> OrderedMap k a -> a
|
||||
findWithDefault a k = Map.findWithDefault a k . omMap
|
||||
|
||||
member :: Ord k => k -> OrderedMap k a -> Bool
|
||||
member k = Map.member k . omMap
|
||||
|
||||
notMember :: Ord k => k -> OrderedMap k a -> Bool
|
||||
notMember k = Map.notMember k . omMap
|
||||
|
||||
lookupLT :: Ord k => k -> OrderedMap k v -> Maybe (k, v)
|
||||
lookupLT k = Map.lookupLT k . omMap
|
||||
|
||||
lookupGT :: Ord k => k -> OrderedMap k v -> Maybe (k, v)
|
||||
lookupGT k = Map.lookupGT k . omMap
|
||||
|
||||
lookupLE :: Ord k => k -> OrderedMap k v -> Maybe (k, v)
|
||||
lookupLE k = Map.lookupLE k . omMap
|
||||
|
||||
lookupGE :: Ord k => k -> OrderedMap k v -> Maybe (k, v)
|
||||
lookupGE k = Map.lookupGE k . omMap
|
||||
|
||||
null :: OrderedMap k a -> Bool
|
||||
null = Map.null . omMap
|
||||
|
||||
size :: OrderedMap k a -> Int
|
||||
size = Map.size . omMap
|
||||
|
||||
map :: (a -> b) -> OrderedMap k a -> OrderedMap k b
|
||||
map f = mapWithKey $ const f
|
||||
|
||||
mapWithKey :: (k -> a -> b) -> OrderedMap k a -> OrderedMap k b
|
||||
mapWithKey f m = m {omMap = Map.mapWithKey f $ omMap m}
|
||||
|
||||
elems :: Ord k => OrderedMap k a -> [a]
|
||||
elems = L.map snd . assocs
|
||||
|
||||
keys :: OrderedMap k a -> [k]
|
||||
keys = omOrder
|
||||
|
||||
assocs :: Ord k => OrderedMap k a -> [(k, a)]
|
||||
assocs = toList
|
||||
|
||||
keysSet :: OrderedMap k a -> Set.Set k
|
||||
keysSet = Map.keysSet . omMap
|
||||
|
||||
toList :: Ord k => OrderedMap k a -> [(k, a)]
|
||||
toList m = L.map (\k -> (k, omMap m Map.! k)) $ omOrder m
|
||||
|
||||
toMap :: OrderedMap k a -> Map.Map k a
|
||||
toMap = omMap
|
||||
|
||||
filter :: Ord k => (a -> Bool) -> OrderedMap k a -> OrderedMap k a
|
||||
filter f m =
|
||||
let newMap = Map.filter f $ omMap m
|
||||
newOrder = L.filter (`Set.member` Map.keysSet newMap) $ omOrder m
|
||||
in OrderedMap {omMap = newMap, omOrder = newOrder}
|
||||
Loading…
Add table
Add a link
Reference in a new issue