{"id":17325667,"url":"https://github.com/tessil/ordered-map","last_synced_at":"2025-04-09T05:09:10.614Z","repository":{"id":60721912,"uuid":"83252419","full_name":"Tessil/ordered-map","owner":"Tessil","description":"C++ hash map and hash set which preserve the order of insertion","archived":false,"fork":false,"pushed_at":"2024-09-22T10:46:48.000Z","size":1033,"stargazers_count":515,"open_issues_count":10,"forks_count":67,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-10-16T14:14:00.401Z","etag":null,"topics":["c-plus-plus","cpp","data-structures","hash-map","hash-table","header-only"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Tessil.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-27T00:36:15.000Z","updated_at":"2024-10-14T11:42:11.000Z","dependencies_parsed_at":"2024-10-25T19:13:53.050Z","dependency_job_id":"b9d60d2c-00b4-47e3-8bd3-e4e83209d32a","html_url":"https://github.com/Tessil/ordered-map","commit_stats":{"total_commits":142,"total_committers":7,"mean_commits":"20.285714285714285","dds":0.04929577464788737,"last_synced_commit":"0557eb58f206aa48bc7fb5f79efdf40b23a66aec"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tessil%2Fordered-map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tessil%2Fordered-map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tessil%2Fordered-map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tessil%2Fordered-map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tessil","download_url":"https://codeload.github.com/Tessil/ordered-map/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247980837,"owners_count":21027808,"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":["c-plus-plus","cpp","data-structures","hash-map","hash-table","header-only"],"created_at":"2024-10-15T14:14:01.308Z","updated_at":"2025-04-09T05:09:10.592Z","avatar_url":"https://github.com/Tessil.png","language":"C++","readme":"[![CI](https://github.com/Tessil/ordered-map/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Tessil/ordered-map/actions/workflows/ci.yml)\n\n## C++ hash map and hash set which preserves the order of insertion\n\nThe ordered-map library provides a hash map and a hash set which preserve the order of insertion in a way similar to Python's [OrderedDict](https://docs.python.org/3/library/collections.html#collections.OrderedDict). When iterating over the map, the values will be returned in the same order as they were inserted.\n\nThe values are stored contiguously in an underlying structure, no holes in-between values even after an erase operation. By default a `std::deque` is used for this structure, but it's also possible to  use a `std::vector`. This structure is directly accessible through the `values_container()` method and if the structure is a `std::vector`, a `data()` method is also provided to easily interact with C APIs. This provides fast iteration but with the drawback of an O(bucket_count) erase operation. An O(1) `pop_back()` and an O(1) `unordered_erase()` functions are available. **If ordered erase is often used, another data structure is recommended.**\n\nTo resolve collisions on hashes, the library uses linear robin hood probing with backward shift deletion.\n\nThe library provides a behaviour similar to a `std::deque/std::vector` with unique values but with an average time complexity of O(1) for lookups and an amortised time complexity of O(1) for insertions. This comes at the price of a little higher memory footprint (8 bytes per bucket by default).\n\nTwo classes are provided: `tsl::ordered_map` and `tsl::ordered_set`.\n\n**Note**: The library uses a power of two for the size of its buckets array to take advantage of the [fast modulo](https://en.wikipedia.org/wiki/Modulo_operation#Performance_issues). For good performances, it requires the hash table to have a well-distributed hash function. If you encounter performance issues check your hash function.\n\n### Key features\n\n- Header-only library, just add the [include](include/) directory to your include path and you are ready to go. If you use CMake, you can also use the `tsl::ordered_map` exported target from the [CMakeLists.txt](CMakeLists.txt).\n- Values are stored in the same order as the insertion order. The library provides a direct access to the underlying structure which stores the values.\n- O(1) average time complexity for lookups with performances similar to `std::unordered_map` but with faster insertions and reduced memory usage (see [benchmark](https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html) for details).\n- Provide random access iterators and also reverse iterators.\n- Support for heterogeneous lookups allowing the usage of `find` with a type different than `Key` (e.g. if you have a map that uses `std::unique_ptr\u003cfoo\u003e` as key, you can use a `foo*` or a `std::uintptr_t` as key parameter to `find` without constructing a `std::unique_ptr\u003cfoo\u003e`, see [example](#heterogeneous-lookups)).\n- If the hash is known before a lookup, it is possible to pass it as parameter to speed-up the lookup (see `precalculated_hash` parameter in [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html#a7fcde27edc6697a0b127f4b1aefa8a7d)).\n- Support for efficient serialization and deserialization (see [example](#serialization) and the `serialize/deserialize` methods in the [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html) for details).\n- The library can be used with exceptions disabled (through `-fno-exceptions` option on Clang and GCC, without an `/EH` option on MSVC or simply by defining `TSL_NO_EXCEPTIONS`). `std::terminate` is used in replacement of the `throw` instruction when exceptions are disabled.\n- API closely similar to `std::unordered_map` and `std::unordered_set`.\n\n### Differences compared to `std::unordered_map`\n`tsl::ordered_map` tries to have an interface similar to `std::unordered_map`, but some differences exist.\n- The iterators are `RandomAccessIterator`.\n- Iterator invalidation behaves in a way closer to `std::vector` and `std::deque` (see [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html#details) for details). If you use `std::vector` as `ValueTypeContainer`, you can use `reserve()` to preallocate some space and avoid the invalidation of the iterators on insert.\n- Slow `erase()` operation, it has a complexity of O(bucket_count). A faster O(1) version `unordered_erase()` exists, but it breaks the insertion order (see [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html#a9f94a7889fa7fa92eea41ca63b3f98a4) for details). An O(1) `pop_back()` is also available.\n- The equality operators `operator==` and `operator!=` are order dependent. Two `tsl::ordered_map` with the same values but inserted in a different order don't compare equal.\n- For iterators, `operator*()` and `operator-\u003e()` return a reference and a pointer to `const std::pair\u003cKey, T\u003e` instead of `std::pair\u003cconst Key, T\u003e` making the value `T` not modifiable. To modify the value you have to call the `value()` method of the iterator to get a mutable reference. Example:\n```c++\ntsl::ordered_map\u003cint, int\u003e map = {{1, 1}, {2, 1}, {3, 1}};\nfor(auto it = map.begin(); it != map.end(); ++it) {\n    //it-\u003esecond = 2; // Illegal\n    it.value() = 2; // Ok\n}\n```\n- By default the map can only hold up to 2\u003csup\u003e32\u003c/sup\u003e - 1 values, that is 4 294 967 295 values. This can be raised through the `IndexType` class template parameter, check the [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html#details) for details. \n- No support for some bucket related methods (like `bucket_size`, `bucket`, ...).\n\nThread-safety guarantee is the same as `std::unordered_map`  (i.e. possible to have multiple concurrent readers with no writer).\n\nThese differences also apply between `std::unordered_set` and `tsl::ordered_set`.\n\n### Exception Guarantees\n\nIf not mentioned otherwise, functions have the strong exception guarantee, see [details](https://en.cppreference.com/w/cpp/language/exceptions). We below list cases in which this guarantee is not provided.\n\nThe guarantee is only provided if `ValueContainer::emplace_back` has the strong exception guarantee (which is true for `std::vector` and `std::deque` as long as the type `T` is not a move-only type with a move constructor that may throw an exception, see [details](http://en.cppreference.com/w/cpp/container/vector/emplace_back#Exceptions)).\n\nThe `tsl::ordered_map::erase_if` and `tsl::ordered_set::erase_if` functions only have the guarantee under the preconditions listed in their documentation.\n\n### Installation\n\nTo use ordered-map, just add the [include](include/) directory to your include path. It is a **header-only** library.\n\nIf you use CMake, you can also use the `tsl::ordered_map` exported target from the [CMakeLists.txt](CMakeLists.txt) with `target_link_libraries`. \n```cmake\n# Example where the ordered-map project is stored in a third-party directory\nadd_subdirectory(third-party/ordered-map)\ntarget_link_libraries(your_target PRIVATE tsl::ordered_map)  \n```\n\nIf the project has been installed through `make install`, you can also use `find_package(tsl-ordered-map REQUIRED)` instead of `add_subdirectory`.\n\nThe code should work with any C++11 standard-compliant compiler and has been tested with GCC 4.8.4, Clang 3.5.0 and Visual Studio 2015.\n\nTo run the tests you will need the Boost Test library and CMake.\n\n```bash\ngit clone https://github.com/Tessil/ordered-map.git\ncd ordered-map/tests\nmkdir build\ncd build\ncmake ..\ncmake --build .\n./tsl_ordered_map_tests \n```\n\n### Usage\n\nThe API can be found [here](https://tessil.github.io/ordered-map/).\n\n### Example\n\n```c++\n#include \u003ciostream\u003e\n#include \u003cstring\u003e\n#include \u003ccstdlib\u003e\n#include \u003ctsl/ordered_map.h\u003e\n#include \u003ctsl/ordered_set.h\u003e\n\nint main() {\n    tsl::ordered_map\u003cchar, int\u003e map = {{'d', 1}, {'a', 2}, {'g', 3}};\n    map.insert({'b', 4});\n    map['h'] = 5;\n    map['e'] = 6;\n    \n    map.erase('a');\n    \n    \n    // {d, 1} {g, 3} {b, 4} {h, 5} {e, 6}\n    for(const auto\u0026 key_value : map) {\n        std::cout \u003c\u003c \"{\" \u003c\u003c key_value.first \u003c\u003c \", \" \u003c\u003c key_value.second \u003c\u003c \"}\" \u003c\u003c std::endl;\n    }\n    \n    \n    map.unordered_erase('b');\n    \n    // Break order: {d, 1} {g, 3} {e, 6} {h, 5}\n    for(const auto\u0026 key_value : map) {\n        std::cout \u003c\u003c \"{\" \u003c\u003c key_value.first \u003c\u003c \", \" \u003c\u003c key_value.second \u003c\u003c \"}\" \u003c\u003c std::endl;\n    }\n    \n    \n    for(auto it = map.begin(); it != map.end(); ++it) {\n        //it-\u003esecond += 2; // Not valid.\n        it.value() += 2;\n    }\n    \n    \n    if(map.find('d') != map.end()) {\n        std::cout \u003c\u003c \"Found 'd'.\" \u003c\u003c std::endl;\n    }\n    \n    const std::size_t precalculated_hash = std::hash\u003cchar\u003e()('d');\n    // If we already know the hash beforehand, we can pass it as argument to speed-up the lookup.\n    if(map.find('d', precalculated_hash) != map.end()) {\n        std::cout \u003c\u003c \"Found 'd' with hash \" \u003c\u003c precalculated_hash \u003c\u003c \".\" \u003c\u003c std::endl;\n    }\n    \n    \n    tsl::ordered_set\u003cchar, std::hash\u003cchar\u003e, std::equal_to\u003cchar\u003e,\n                     std::allocator\u003cchar\u003e, std::vector\u003cchar\u003e\u003e set;\n    set.reserve(6);\n    \n    set = {'3', '4', '9', '2'};\n    set.erase('2');\n    set.insert('1');\n    set.insert('\\0');\n    \n    set.pop_back();\n    set.insert({'0', '\\0'});\n    \n    // Get raw buffer for C API: 34910\n    std::cout \u003c\u003c atoi(set.data()) \u003c\u003c std::endl;\n}\n```\n\n#### Heterogeneous lookup\n\nHeterogeneous overloads allow the usage of other types than `Key` for lookup and erase operations as long as the used types are hashable and comparable to `Key`.\n\nTo activate the heterogeneous overloads in `tsl::ordered_map/set`, the qualified-id `KeyEqual::is_transparent` must be valid. It works the same way as for [`std::map::find`](http://en.cppreference.com/w/cpp/container/map/find). You can either use [`std::equal_to\u003c\u003e`](http://en.cppreference.com/w/cpp/utility/functional/equal_to_void) or define your own function object.\n\nBoth `KeyEqual` and `Hash` will need to be able to deal with the different types.\n\n```c++\n#include \u003cfunctional\u003e\n#include \u003ciostream\u003e\n#include \u003cstring\u003e\n#include \u003ctsl/ordered_map.h\u003e\n\n\n\nstruct employee {\n    employee(int id, std::string name) : m_id(id), m_name(std::move(name)) {\n    }\n    \n    // Either we include the comparators in the class and we use `std::equal_to\u003c\u003e`...\n    friend bool operator==(const employee\u0026 empl, int empl_id) {\n        return empl.m_id == empl_id;\n    }\n    \n    friend bool operator==(int empl_id, const employee\u0026 empl) {\n        return empl_id == empl.m_id;\n    }\n    \n    friend bool operator==(const employee\u0026 empl1, const employee\u0026 empl2) {\n        return empl1.m_id == empl2.m_id;\n    }\n    \n    \n    int m_id;\n    std::string m_name;\n};\n\n// ... or we implement a separate class to compare employees.\nstruct equal_employee {\n    using is_transparent = void;\n    \n    bool operator()(const employee\u0026 empl, int empl_id) const {\n        return empl.m_id == empl_id;\n    }\n    \n    bool operator()(int empl_id, const employee\u0026 empl) const {\n        return empl_id == empl.m_id;\n    }\n    \n    bool operator()(const employee\u0026 empl1, const employee\u0026 empl2) const {\n        return empl1.m_id == empl2.m_id;\n    }\n};\n\nstruct hash_employee {\n    std::size_t operator()(const employee\u0026 empl) const {\n        return std::hash\u003cint\u003e()(empl.m_id);\n    }\n    \n    std::size_t operator()(int id) const {\n        return std::hash\u003cint\u003e()(id);\n    }\n};\n\n\nint main() {\n    // Use std::equal_to\u003c\u003e which will automatically deduce and forward the parameters\n    tsl::ordered_map\u003cemployee, int, hash_employee, std::equal_to\u003c\u003e\u003e map; \n    map.insert({employee(1, \"John Doe\"), 2001});\n    map.insert({employee(2, \"Jane Doe\"), 2002});\n    map.insert({employee(3, \"John Smith\"), 2003});\n\n    // John Smith 2003\n    auto it = map.find(3);\n    if(it != map.end()) {\n        std::cout \u003c\u003c it-\u003efirst.m_name \u003c\u003c \" \" \u003c\u003c it-\u003esecond \u003c\u003c std::endl;\n    }\n\n    map.erase(1);\n\n\n\n    // Use a custom KeyEqual which has an is_transparent member type\n    tsl::ordered_map\u003cemployee, int, hash_employee, equal_employee\u003e map2;\n    map2.insert({employee(4, \"Johnny Doe\"), 2004});\n\n    // 2004\n    std::cout \u003c\u003c map2.at(4) \u003c\u003c std::endl;\n} \n```\n\n#### Serialization\n\nThe library provides an efficient way to serialize and deserialize a map or a set so that it can be saved to a file or send through the network.\nTo do so, it requires the user to provide a function object for both serialization and deserialization.\n\n```c++\nstruct serializer {\n    // Must support the following types for U: std::uint64_t, float \n    // and std::pair\u003cKey, T\u003e if a map is used or Key for a set.\n    template\u003ctypename U\u003e\n    void operator()(const U\u0026 value);\n};\n```\n\n```c++\nstruct deserializer {\n    // Must support the following types for U: std::uint64_t, float \n    // and std::pair\u003cKey, T\u003e if a map is used or Key for a set.\n    template\u003ctypename U\u003e\n    U operator()();\n};\n```\n\nNote that the implementation leaves binary compatibility (endianness, float binary representation, size of int, ...) of the types it serializes/deserializes in the hands of the provided function objects if compatibility is required.\n\nMore details regarding the `serialize` and `deserialize` methods can be found in the [API](https://tessil.github.io/ordered-map/classtsl_1_1ordered__map.html).\n\n```c++\n#include \u003ccassert\u003e\n#include \u003ccstdint\u003e\n#include \u003cfstream\u003e\n#include \u003ctype_traits\u003e\n#include \u003ctsl/ordered_map.h\u003e\n\n\nclass serializer {\npublic:\n    explicit serializer(const char* file_name) {\n        m_ostream.exceptions(m_ostream.badbit | m_ostream.failbit);\n        m_ostream.open(file_name, std::ios::binary);\n    }\n    \n    template\u003cclass T,\n             typename std::enable_if\u003cstd::is_arithmetic\u003cT\u003e::value\u003e::type* = nullptr\u003e\n    void operator()(const T\u0026 value) {\n        m_ostream.write(reinterpret_cast\u003cconst char*\u003e(\u0026value), sizeof(T));\n    }\n    \n    void operator()(const std::pair\u003cstd::int64_t, std::int64_t\u003e\u0026 value) {\n        (*this)(value.first);\n        (*this)(value.second);\n    }\n\nprivate:\n    std::ofstream m_ostream;\n};\n\nclass deserializer {\npublic:\n    explicit deserializer(const char* file_name) {\n        m_istream.exceptions(m_istream.badbit | m_istream.failbit | m_istream.eofbit);\n        m_istream.open(file_name, std::ios::binary);\n    }\n    \n    template\u003cclass T\u003e\n    T operator()() {\n        T value;\n        deserialize(value);\n        \n        return value;\n    }\n    \nprivate:\n    template\u003cclass T,\n             typename std::enable_if\u003cstd::is_arithmetic\u003cT\u003e::value\u003e::type* = nullptr\u003e\n    void deserialize(T\u0026 value) {\n        m_istream.read(reinterpret_cast\u003cchar*\u003e(\u0026value), sizeof(T));\n    }\n    \n    void deserialize(std::pair\u003cstd::int64_t, std::int64_t\u003e\u0026 value) {\n        deserialize(value.first);\n        deserialize(value.second);\n    }\n\nprivate:\n    std::ifstream m_istream;\n};\n\n\nint main() {\n    const tsl::ordered_map\u003cstd::int64_t, std::int64_t\u003e map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}};\n    \n    \n    const char* file_name = \"ordered_map.data\";\n    {\n        serializer serial(file_name);\n        map.serialize(serial);\n    }\n    \n    {\n        deserializer dserial(file_name);\n        auto map_deserialized = tsl::ordered_map\u003cstd::int64_t, std::int64_t\u003e::deserialize(dserial);\n        \n        assert(map == map_deserialized);\n    }\n    \n    {\n        deserializer dserial(file_name);\n        \n        /**\n         * If the serialized and deserialized map are hash compatibles (see conditions in API), \n         * setting the argument to true speed-up the deserialization process as we don't have \n         * to recalculate the hash of each key. We also know how much space each bucket needs.\n         */\n        const bool hash_compatible = true;\n        auto map_deserialized = \n            tsl::ordered_map\u003cstd::int64_t, std::int64_t\u003e::deserialize(dserial, hash_compatible);\n        \n        assert(map == map_deserialized);\n    }\n} \n```\n\n##### Serialization with Boost Serialization and compression with zlib\n\nIt's possible to use a serialization library to avoid the boilerplate. \n\nThe following example uses Boost Serialization with the Boost zlib compression stream to reduce the size of the resulting serialized file. The example requires C++20 due to the usage of the template parameter list syntax in lambdas, but it can be adapted to less recent versions.\n\n```c++\n#include \u003cboost/archive/binary_iarchive.hpp\u003e\n#include \u003cboost/archive/binary_oarchive.hpp\u003e\n#include \u003cboost/iostreams/filter/zlib.hpp\u003e\n#include \u003cboost/iostreams/filtering_stream.hpp\u003e\n#include \u003cboost/serialization/split_free.hpp\u003e\n#include \u003cboost/serialization/utility.hpp\u003e\n#include \u003ccassert\u003e\n#include \u003ccstdint\u003e\n#include \u003cfstream\u003e\n#include \u003ctsl/ordered_map.h\u003e\n\n\nnamespace boost { namespace serialization {\n    template\u003cclass Archive, class Key, class T\u003e\n    void serialize(Archive \u0026 ar, tsl::ordered_map\u003cKey, T\u003e\u0026 map, const unsigned int version) {\n        split_free(ar, map, version); \n    }\n\n    template\u003cclass Archive, class Key, class T\u003e\n    void save(Archive \u0026 ar, const tsl::ordered_map\u003cKey, T\u003e\u0026 map, const unsigned int /*version*/) {\n        auto serializer = [\u0026ar](const auto\u0026 v) { ar \u0026 v; };\n        map.serialize(serializer);\n    }\n\n    template\u003cclass Archive, class Key, class T\u003e\n    void load(Archive \u0026 ar, tsl::ordered_map\u003cKey, T\u003e\u0026 map, const unsigned int /*version*/) {\n        auto deserializer = [\u0026ar]\u003ctypename U\u003e() { U u; ar \u0026 u; return u; };\n        map = tsl::ordered_map\u003cKey, T\u003e::deserialize(deserializer);\n    }\n}}\n\n\nint main() {\n    tsl::ordered_map\u003cstd::int64_t, std::int64_t\u003e map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}};\n    \n    \n    const char* file_name = \"ordered_map.data\";\n    {\n        std::ofstream ofs;\n        ofs.exceptions(ofs.badbit | ofs.failbit);\n        ofs.open(file_name, std::ios::binary);\n        \n        boost::iostreams::filtering_ostream fo;\n        fo.push(boost::iostreams::zlib_compressor());\n        fo.push(ofs);\n        \n        boost::archive::binary_oarchive oa(fo);\n        \n        oa \u003c\u003c map;\n    }\n    \n    {\n        std::ifstream ifs;\n        ifs.exceptions(ifs.badbit | ifs.failbit | ifs.eofbit);\n        ifs.open(file_name, std::ios::binary);\n        \n        boost::iostreams::filtering_istream fi;\n        fi.push(boost::iostreams::zlib_decompressor());\n        fi.push(ifs);\n        \n        boost::archive::binary_iarchive ia(fi);\n     \n        tsl::ordered_map\u003cstd::int64_t, std::int64_t\u003e map_deserialized;   \n        ia \u003e\u003e map_deserialized;\n        \n        assert(map == map_deserialized);\n    }\n}\n```\n\n### License\n\nThe code is licensed under the MIT license, see the [LICENSE file](LICENSE) for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessil%2Fordered-map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftessil%2Fordered-map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessil%2Fordered-map/lists"}