Search code examples
c++range-v3

Using range::find on a view


I'd like to rangify the following code, which checks for the first occurance of a sequence of unique characters:

bool hasOnlyUniqueElements( auto& data ) {
    std::unordered_set<char> set;

    for( auto& value : data )
        set.emplace( value );

    return set.size() == data.size();
}

int64_t getStartPacketMarker( const std::string& data, int64_t markerSize ) {
    for( int64_t i = 0; i < data.size() - markerSize; i++ )
    {
        std::string_view packet( data.begin() + i, data.begin() + i + markerSize );
        if( hasOnlyUniqueElements( packet ) )
            return i + markerSize;
    }
    return -1;
}

I came up with the following, that uses ranges but is only marginally better:

int64_t getStartPacketMarker( const std::string& data, int64_t markerSize ) {
    int64_t idx = 0;
    for( auto packet :  data | ranges::views::sliding( markerSize ) ) {
        if( hasOnlyUniqueElements( packet ) )
            return idx + markerSize;
        idx++;
    }

    return -1;
}

This should be a simple find operation, but I couldn't make it work and couldn't find any examples on find being used on views. Is it possible to use find on views?


Solution

  • Yes, you can use find on views. However, in your case, you should use find_if since you are checking against a predicate function:

    auto view = data | std::views::slide(markerSize);
    auto it = std::ranges::find_if(
                  view, somePredicate
              );
    return it == view.end() ? -1 : it - view.begin();
    

    However, since your predicate function has an auto-deduced parameter, you can't get the function pointer of it directly, and you would need to wrap it in a lambda instead:

    auto view = data | std::views::slide(markerSize);
    auto it = std::ranges::find_if(
                  view, [](const auto& v) { return hasOnlyUniqueElements(v); }
              );
    return it == view.end() ? -1 : it - view.begin();