0

ALL,

I have a following structure

struct Definition
{
    std::string schema, name;
    Definition(const std::string &s, const std::string n) : schema(s), name(n) {}
}

and I have a following class

class Foo
{
public:
    Foo()
    {
        myVector.push_back( "abc", "def" );
        myVector.push_back( "abc", "ghi" );
        myVector.push_back( "abc", "jkl" );
    }
    void EditVector();
private:
    std::vector<Definition> myVector;
};

void Foo::EditVector()
{
    for( auto i = 0; i < somedata.size(); ++i )
    {
        // if somedata[i] is not found inside mtVector name - add it
        // if somedata.size() is less than myVector - remove the extra element
    }
}

Now adding the element not found is easy - just do find_if() and if it returns myVector.end() - call push_back().

But what about removing?

I thought I can save myVector to a temporary one, then remove the element from that temporary and finally remove elements from myVector that are left inside the temporary one. But how do I do that? Or maybe there is a better and more elegant solution?

The somedata is a dynamic vector and its content is not known at the time.

TIA!!

I did see the answer here but it uses simple/basic type. And maybe there is even more elegant solution.

EDIT:

As apparently something is not clear - here is an explanation.

somedata vector contains just the names.

It may have "def" and "ghi" only or it may have "def", "ghi, "jkl", "klm", "nop" and "qrs".

In the first case - "jkl" is not there (see the constructor), so when the function EditVector() ends myVector should not contain that element. And in the second case 3 elements should be added to the vector myVector with the schema "abc".

EDIT2:

I made it a following function:

void RemoveTableFromVector(const std::vector<TableDefinition> &temp)
{
    myVector.erase( std::remove_if( myVector.begin(), myVector.end(),
               [&](const Definition &d)
               {
                     return std::find( temp.cbegin(), temp.cend(), d.name ) == temp.cend();
               } ), myVector.end() );
}

and I got the error binary == no operator found, which takes a left-hand operator of type const Definition (or there is no acceptable conversion).

Igor
  • 5,620
  • 11
  • 51
  • 103
  • Please explain, what do you want to remove? What is `somedata`? What is an extra element? – 273K Aug 05 '22 at 14:54
  • 1
    You probably shouldn't be using `vector `. Maybe something like `set`. – n. m. could be an AI Aug 07 '22 at 06:27
  • Please explain why your `EditVector` implementation isn't just `myVector = somedata;`. I'm in agreement with the other comment above - you probably want to be using std::set or std::map (or the unordered variants). – selbie Aug 07 '22 at 06:33
  • @selbie, because myvector and somedata may contain different data. Myvector contains set of strings whichanges based on mydata content. However I now realize that after reading the data inside somedata I can simply do: `myvector = somedata` – Igor Aug 07 '22 at 07:41

1 Answers1

0

Let's see the building blocks. retain_all algorithm based on this answer:

template<class ForwardIt, class InputIt, class Compare = std::less<>>
ForwardIt retain_all(ForwardIt first1, ForwardIt last1,
                     InputIt first2, InputIt last2,
                     Compare comp = std::less{})
{
    auto newEnd = std::remove_if(
        first1, last1,
        [&](const auto& x) {
            while (first2 != last2 && comp(*first2, x))
                ++first2;
            return first2 == last2 || comp(x, *first2);
        });
    return newEnd;
}

For remove_all, std::set_difference is almost suitable, but it will only remove a value at most as many times as it's present in the second set. I slightly modified the example implementation:

template<class InputIt1, class InputIt2,
         class OutputIt, class Compare>
OutputIt remove_all_copy(InputIt1 first1, InputIt1 last1,
                         InputIt2 first2, InputIt2 last2,
                         OutputIt d_first, Compare comp)
{
    while (first1 != last1) {
        if (first2 == last2)
            return std::copy(first1, last1, d_first);
 
        if (comp(*first1, *first2)) {
            *d_first++ = *first1++;
        } else if (comp(*first2, *first1)) {
            ++first2;
        } else {
            ++first1;
        }
    }
    return d_first;
}

Convenience wrappers that accept containers:

template<class C1, class C2, class Compare = std::less<>>
void retainAll(C1& a, const C2& b, Compare cmp = std::less{}) {
    a.erase(
        retain_all(a.begin(), a.end(), b.begin(), b.end(), cmp),
        a.end());
}

template<class C1, class C2, class Compare = std::less<>>
C1 removeAllCopy(const C1& a, const C2& b, Compare comp = std::less{})
{
    C1 result;
    remove_all_copy(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(result), comp);
    return result;
}

Editing myVector:

void Foo::EditVector()
{
    std::vector<std::string> somedata{"def", "ghi", "klm"};

    // Comparator that works with any combination of std::strings and Definitions
    auto cmp = [](const auto& a, const auto& b) {
        struct {
            const std::string& operator()(const std::string& x) { return x; }
            const std::string& operator()(const Definition& x) { return x.name; }
        } key;
        return key(a) < key(b);
    };

    // The input need to be sorted
    std::sort(somedata.begin(), somedata.end());
    std::sort(myVector.begin(), myVector.end(), cmp);

    // Delete Definitions whose name is not in somedata
    retainAll(myVector, somedata, cmp);

    // Add new Definitions
    auto toAdd = removeAllCopy(somedata, myVector, cmp);
    for (const auto& name : toAdd) {
        myVector.emplace_back("abc", name);
    }
}

Or with only using removal algorithm:

// Removal
auto toRemove = removeAllCopy(myVector, somedata, cmp);
myVector = removeAllCopy(myVector, toRemove, cmp);

// Addition
auto toAdd = removeAllCopy(somedata, myVector, cmp);
// range for loop same

Without sorting

Simple, O(nm) solution:

// Erase elements
myVector.erase(std::remove_if(myVector.begin(), myVector.end(), [&](const Definition& d) {
    return std::find(somedata.begin(), somedata.end(), d.name) == somedata.end();
}), myVector.end());

// Add new definitions
for (const std::string& name : somedata) {
    auto it = std::find_if(myVector.begin(), myVector.end(), [&](const Definition& d) {
        return d.name == name;
    });
    if (it == myVector.end())
        myVector.emplace_back("abc", name);
}
krisz
  • 2,686
  • 2
  • 11
  • 18
  • I understand you are trying to create a generic solution. `soedata` will contain strings only once. So the first function of `retain_all` should bu sufficient. Also, IIUC, the comparator can be declared as part the class, right? Just trying to expose the least possible stuff... – Igor Aug 06 '22 at 03:59
  • Yes, `retain_all` + `std::set_difference` should be sufficient. In earlier design, I used `remove_all` for `myVector` as well, but in the final solution it looks like it's not needed. Comparison operators can be added to the class, but they have to sort first by `name`. – krisz Aug 06 '22 at 04:16
  • is there a way to not to se comparators? I don't need those vectors sorted - I'm happy as it is... – Igor Aug 06 '22 at 04:50
  • I added a method that doesn't use sorting. – krisz Aug 06 '22 at 14:42
  • the modification relies of the `const Definition operator==()` to be present. That's what my MSVC 2017 complains about `no operator found found which takes a left hand operand of type const Definition (or there is no acceptable conversion`. – Igor Aug 07 '22 at 02:28
  • What do you mean by "modification"? The code compiles fine for me. – krisz Aug 07 '22 at 03:32
  • @kirz, strange. Did you remove me conparator? – Igor Aug 07 '22 at 04:05
  • Which code snippet are you referring to? The original with `retain_all` or the last one that doesn't use sorting? None of this code uses `Definition::operator==`. – krisz Aug 07 '22 at 04:32
  • @kirz, I refer to the one without the comparator for deletion. – Igor Aug 07 '22 at 04:54
  • @kirz, I added the code I'm trying to compile... Please see my second edit. – Igor Aug 07 '22 at 05:53
  • gcc is a little more clear - you code compares `Definition` and `string` inside the `std::find`. – Igor Aug 07 '22 at 06:43
  • My code doesn't, but your code compares `TableDefinition` and `string`. What's `TableDefinition`? – krisz Aug 07 '22 at 16:44