Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change_key(itr, key) function #116

Open
Auburn opened this issue Apr 11, 2024 · 0 comments
Open

change_key(itr, key) function #116

Auburn opened this issue Apr 11, 2024 · 0 comments
Assignees
Labels
enhancement New feature or request

Comments

@Auburn
Copy link

Auburn commented Apr 11, 2024

For my specific use case I require being able to change the key for a given element without changing it's index in the underlying array. As far as I can tell this isn't possible with the current API.

So I wrote a change_key function which takes the iterator of the element you want to change and a new key for it. If the new key already exists it doesn't make any change and returns the existing new key iterator, otherwise it returns the now updated iterator with it's new key.

I amalgamated the function from sections of emplace and erase, I wrote a few tests for it and it seems to work correctly. No idea if it's ideal optimisation wise but this is what it looks like

    template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true>
    auto change_key(iterator it, Key const& new_key) -> std::pair<iterator, bool>
    {
        auto const value_idx_to_change = static_cast<value_idx_type>(it - cbegin());
        
        auto new_hash = mixed_hash(new_key);
        auto new_bucket_idx = bucket_idx_from_hash( new_hash );
        auto dist_and_fingerprint = dist_and_fingerprint_from_hash( new_hash );

        while (true) {
            auto* bucket = &at(m_buckets, new_bucket_idx);
            if (dist_and_fingerprint == bucket->m_dist_and_fingerprint) {
                if (m_equal(new_key, get_key(m_values[bucket->m_value_idx]))) {
                    return {begin() + static_cast<difference_type>(bucket->m_value_idx), false};
                }
            } else if (dist_and_fingerprint > bucket->m_dist_and_fingerprint) {
                // remove old key from buckets
                auto old_hash = mixed_hash(get_key(*it));
                auto old_bucket_idx = bucket_idx_from_hash(old_hash);

                while(at(m_buckets, old_bucket_idx).m_value_idx != value_idx_to_change)
                {
                    old_bucket_idx = next(old_bucket_idx);
                }

                // shift down until either empty or an element with correct spot is found
                auto next_bucket_idx = next(old_bucket_idx);
                while(at(m_buckets, next_bucket_idx).m_dist_and_fingerprint >= Bucket::dist_inc * 2)
                {
                    at(m_buckets, old_bucket_idx) = {dist_dec(at(m_buckets, next_bucket_idx).m_dist_and_fingerprint),
                                                 at(m_buckets, next_bucket_idx).m_value_idx};
                    old_bucket_idx = std::exchange(next_bucket_idx, next(next_bucket_idx));
                }
                at(m_buckets, old_bucket_idx) = {};                

                // place new key, with existing value idx
                place_and_shift_up({dist_and_fingerprint, value_idx_to_change}, new_bucket_idx);
                it->first = new_key;
                return {it, true};
            }
            dist_and_fingerprint = dist_inc(dist_and_fingerprint);
            new_bucket_idx = next(new_bucket_idx);
        }           
    }

Basically I wanted to know if the function implementation looks sensible to you. You are free to add it to the project if if you think it would be useful. Thanks

@Auburn Auburn added the enhancement New feature or request label Apr 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants