{"id":22297470,"url":"https://github.com/rustonaut/total-order-multi-map","last_synced_at":"2025-12-12T14:22:23.467Z","repository":{"id":57670257,"uuid":"117703868","full_name":"rustonaut/total-order-multi-map","owner":"rustonaut","description":"A multimap with at the same time keeps the total insertion ordering of all elements","archived":false,"fork":false,"pushed_at":"2019-10-11T14:07:56.000Z","size":56,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-11-05T23:15:05.001Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rustonaut.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-16T15:44:47.000Z","updated_at":"2025-05-29T15:04:12.000Z","dependencies_parsed_at":"2022-09-26T20:40:38.114Z","dependency_job_id":null,"html_url":"https://github.com/rustonaut/total-order-multi-map","commit_stats":null,"previous_names":["1aim/total-order-multi-map"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/rustonaut/total-order-multi-map","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustonaut%2Ftotal-order-multi-map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustonaut%2Ftotal-order-multi-map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustonaut%2Ftotal-order-multi-map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustonaut%2Ftotal-order-multi-map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rustonaut","download_url":"https://codeload.github.com/rustonaut/total-order-multi-map/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rustonaut%2Ftotal-order-multi-map/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27684616,"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","status":"online","status_checked_at":"2025-12-12T02:00:06.775Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-12-03T17:49:51.822Z","updated_at":"2025-12-12T14:22:23.435Z","avatar_url":"https://github.com/rustonaut.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"total-order-multi-map [![Crates.io](https://img.shields.io/crates/v/total-order-multi-map.svg)](https://crates.io/crates/total-order-multi-map) [![total-order-multi-map](https://docs.rs/total-order-multi-map/badge.svg)](https://docs.rs/total-order-multi-map) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n=============\n\nA multi-map with at the same time keeps the total insertion ordering of all elements,\nthis means if you insert following key-value pairs: `(K1, V1)`, `(K2, V2)`, `(K1, V3)`\nIt will remember that the insertion oder was exact this (`V1`, `V2`, `V3`) i.e.\nit won't collapse the insertion order on a per-key basis.\n\nAt the same time it should have similar performance for accessing entries as a normal\n`HashMap` (else we could have just created a `Vec`).\n\nIt does so by limiting its values to such which implement `StableDeref`, e.g.\n`Box\u003cTraitObject\u003e` through which it can have more than one ~reference~ pointer to\na value. While it uses unsafety internally the provided interface is safe.\nIt also makes sure to be unwind + resume safe.\n\nExample\n--------\n\n```rust\nextern crate total_order_multi_map as tomm;\n\nuse std::fmt::{self, Display};\nuse tomm::TotalOrderMultiMap;\n\n/// for now the key has to be copy\ntype Key = \u0026'static str;\n\n/// the map is made for thinks which dereference\n/// e.g. trait objects, through e.g. `String` or\n/// `Rc\u003cDebug\u003e` would be fine, too.\ntype Value = Box\u003cDisplay\u003e;\n\n\nfn main() {\n    let mut map = TotalOrderMultiMap::\u003cKey, Value\u003e::new();\n\n    map.add(\"key1\", mk_box_str(\"val1\"));\n    map.add(\"key1\", mk_my_thingy(\"val2\"));\n    map.add(\"key2\", mk_box_str(\"val3\"));\n    map.add(\"key1\", mk_my_thingy(\"val4\"));\n    map.add(\"key0\", mk_box_str(\"val5\"));\n\n    let stringed = map\n        .iter()\n        // val is of type `\u0026Display`\n        .map(|(key, val)| format!(\"{}:{}\", key, val))\n        .collect::\u003cVec\u003c_\u003e\u003e();\n\n    // as it can be seen the total insertion order was kept\n    assert_eq!(stringed, \u0026[\n        \"key1:val1\".to_owned(),\n        \"key1:val2\".to_owned(),\n        \"key2:val3\".to_owned(),\n        \"key1:val4\".to_owned(),\n        \"key0:val5\".to_owned()\n    ]);\n\n    // It's also still a multi map.\n    // Note that the get operation has roughly the same\n    // performance as in a hash map (through you then\n    // iterate over the elements associated with the key\n    // roughly with the speed of iterating over an `Vec`).\n    {\n        let values = map.get(\"key1\").expect(\"key1 is in the map\");\n        let stringed = values\n            .map(|val| format!(\"{}\", val))\n            .collect::\u003cVec\u003c_\u003e\u003e();\n\n        assert_eq!(stringed, \u0026[ \"val1\", \"val2\", \"val4\" ]);\n    }\n\n    // but as we have an order we can remove\n    // \"the last inserted\" element\n    let (key, val) = map.pop().unwrap();\n    assert_eq!(format!(\"{}:{}\", key, val), \"key0:val5\");\n\n    // or remove all for an specific key as it's\n    // an multi map\n    map.remove_all(\"key1\");\n    assert!(map.get(\"key1\").is_none());\n\n    println!(\"DONE (I only contain asserts)\")\n}\n\n//---- some function to create dummy values for the example ----\n\nfn mk_box_str(inp: \u0026str) -\u003e Box\u003cDisplay\u003e {\n    // sadly we can't cast Box\u003cstr\u003e to Box\u003cDisplay\u003e\n    // (you would need non static vtables for this...)\n    Box::new(inp.to_owned())\n}\n\n#[derive(Debug)]\nstruct MyThingy { val: String }\n\nimpl Display for MyThingy {\n    fn fmt(\u0026self, fter: \u0026mut fmt::Formatter) -\u003e fmt::Result {\n        write!(fter, \"{}\", self.val)\n    }\n}\n\nfn mk_my_thingy(inp: \u0026str) -\u003e Box\u003cDisplay\u003e {\n    Box::new(MyThingy { val: inp.to_owned() })\n}\n```\n\nMissing Parts\n-------------\n\nFollowing thinks can be implemented and should\nbe added to further versions:\n\n- benchmarks\n- use `SmallVec` for the multi map\n- allow `\u0026mut` access to `V::Target` if  `V: StableDeref + DerefMut`\n- indexed access\n  - indexed get off key-value pairs\n  - indexed remove\n  - indexed get _in_ the return value of `get`, i.e. a multi map group\n- improved entry api\n  - allow access to other values for same key\n  - potentially turn the result of entry + insert into the result of get + unwrap\n- improved grouped iteration, maybe??\n  - the current implementation groups by key but doesn't indicate\n    when it switches to the next value\n- allow non-copy keys??\n  - we still don't want to duplicate/clone keys so we would have to do\n    some trickery there\n\nLicense\n-------\n\nLicensed under either of\n\n- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\nContribution\n------------\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall\nbe dual licensed as above, without any additional terms or conditions.\n\n\nChange Log\n-----------\n\n- `v0.3`:\n  - `TotalOrderMultiMap.retain` now accepts a predicate accepting `\u0026V::Target` instead\n    of `\u0026V`\n- `v0.4.1`:\n  - flatten return value to `get` to return empty iterators instead\n    of `None`\n  - requires `DerefMut` instead of just `Deref`\n  - has mutable access methods like `get_mut`\n  - split `insert` into `add` and `set` where `set` replaces any value\n    associated with the key prev. with the new value returning the old\n    value\n- `v0.4.2`:\n  - mut iter to non mut iter conversions\n    - Iter from IterMut\n    - Values from ValuesMut\n    - EntryValues from EntryValuesMut\n- `v0.4.3`:\n  - Added missing re-exports. Values, Values,\n    GroupedValuesMut are now public as they\n    should have been from the beginning.\n- `v0.4.4`:\n  - Added `truncate` method allowing the removal\n    of any think inserted after the first `size`\n    elements\n- `v0.4.5`:\n  - For entries added `values`,`values_mut` methods to\n    iterate over the values associated with the key used\n    to create the entry.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustonaut%2Ftotal-order-multi-map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frustonaut%2Ftotal-order-multi-map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustonaut%2Ftotal-order-multi-map/lists"}