{"id":23651500,"url":"https://github.com/athanclark/timemap","last_synced_at":"2025-06-30T11:34:09.964Z","repository":{"id":56880612,"uuid":"47613982","full_name":"athanclark/timemap","owner":"athanclark","description":"A map structure for UTCTime-indexed entities","archived":false,"fork":false,"pushed_at":"2018-04-06T07:58:00.000Z","size":2494,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-03T09:26:07.033Z","etag":null,"topics":["expiration","haskell","timemap"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/athanclark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-08T10:03:36.000Z","updated_at":"2018-11-27T20:23:53.000Z","dependencies_parsed_at":"2022-08-20T23:10:39.120Z","dependency_job_id":null,"html_url":"https://github.com/athanclark/timemap","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/athanclark/timemap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Ftimemap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Ftimemap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Ftimemap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Ftimemap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/athanclark","download_url":"https://codeload.github.com/athanclark/timemap/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Ftimemap/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259371203,"owners_count":22847536,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expiration","haskell","timemap"],"created_at":"2024-12-28T16:38:07.735Z","updated_at":"2025-06-12T00:34:47.959Z","avatar_url":"https://github.com/athanclark.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"timemap\n=======\n\nA mutable `Data.HashMap`-like structure, where each entity is implicitly indexed\nby their last-modified time. Useful for keyed caches.\n\n## Usage\n\n```haskell\nimport qualified Data.TimeMap as TM\nimport Control.Concurrent.STM\n\n\nmain :: IO ()\nmain = do\n  -- create a new, empty reference\n  mapRef \u003c- atomically TM.newTimeMap\n\n  -- insert a new key/value pair in the map. Note that\n  -- `someKey` should implement `Hashable`. This also\n  -- sets the creation time of the value to \"now\".\n  TM.insert someKey 0 mapRef\n\n  -- adjusts the value of `someKey` to `1`. Note that\n  -- also resets the creation time of the value to \"now\".\n  TM.adjust (+1) someKey mapRef\n\n  -- wait a second\n  threadDelay 1000000\n\n  -- delete all values older than 1 second from now.\n  atomically $ TM.filterFromNow 1 mapRef\n\n  -- Will return `Nothing`\n  atomically $ TM.lookup someKey mapRef\n\n  return ()\n```\n\n## How it works\n\nThere are two internal maps for a `TimeMap k a`:\n\n- a \"time-indexed\" map reference: `TVar (Map UTCTime (HashSet k))`\n- a mutable map of values: `STMContainers.Map.Map k (UTCTime, a)`\n\n### Insertion\n\nInserting a new value first performs a lookup on the hashtable to see if the key\nalready exists:\n\n- If it doesn't, insert the current time and the element that into the mutable map.\n  Then, insert the _key_ as the value, and the _time_ as the key into\n  the time-indexed multimap.\n- If it does, remove the entry from the time-indexed multimap for the old time, before\n  doing the same thing as the first bullet.\n\n### Lookups\n\nThis doesn't have to create new data, and thus doesn't need full `IO`. All it does is\nlookup the key in the mutable map.\n\n### Filtering\n\nTo filter out all entries older than some time, you have to use the `Data.Map.splitLookup`\nfunction to split the time-indexed multimap into the entries you want to delete from\nthe main container, and the ones you want to keep. You simply `writeTVar` the map you want\nto keep, but for the ones you want to delete, you need to get all the `elems` of\nthat map. Then, for every key that we need to delete, we delete it from the\nmain container. Suprisingly, this appears to operate in constant time, regardless of\nthe size of the map.\n\n## How to run tests\n\n```bash\nstack test\n```\n\n## Benchmarks\n\n```bash\nstack bench --benchmark-arguments=\"--output profile.html\"\n```\n\nYou can also view the results on my box\n[here](https://htmlpreview.github.io/?https://github.com/athanclark/timemap/blob/master/profile.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathanclark%2Ftimemap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fathanclark%2Ftimemap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathanclark%2Ftimemap/lists"}