I have some entries in the repeated field in my proto. Now I want delete some of them. How can I accomplish this? There is a function to delete the last element, but I want to delete arbitrary elements. I cant just swap them because the order is important.
I could swap with next until end, but isn't there a nicer solution?
According to the API docs, there isn't a way to arbitrarily remove an element from within a repeated field, just a way to remove the last one.
...
We don't provide a way to remove any element other than the last
because it invites inefficient use, such as O(n^2) filtering loops
that should have been O(n). If you want to remove an element other
than the last, the best way to do it is to re-arrange the elements so
that the one you want removed is at the end, then call RemoveLast()
...
Protobuf v2
You can use the DeleteSubrange(int start, int num)
in RepeatedPtrField
class.
So if you want to delete a single element then you have to call this method as DeleteSubrange(index_to_be_del, 1)
. It will remove the element at that index.
Protobuf v3 update
As mentioned in the comments, iterator RepeatedField::erase(const_iterator position)
can delete at arbitrary position
What I usually do in these cases is to create a new Protobuf (PB) message. I iterate the repeated fields of the existing message and add them (except the ones you don't want anymore) to the new PB message.
Here is example:
message GuiChild
{
optional string widgetName = 1;
//..
}
message GuiLayout
{
repeated ChildGuiElement children = 1;
//..
}
typedef google_public::protobuf::RepeatedPtrField<GuiChild> RepeatedField;
typedef google_public::protobuf::Message Msg;
GuiLayout guiLayout;
//Init children as necessary..
GuiChild child;
//Set child fileds..
DeleteElementsFromRepeatedField(*child, guiLayout->mutable_children());
void DeleteElementsFromRepeatedField(const Msg& msg, RepeatedField* repeatedField)
{
for (RepeatedField::iterator it = repeatedField->begin(); it != repeatedField->end(); it++)
{
if (google_public::protobuf::util::MessageDifferencer::Equals(*it, msg))
{
repeatedField->erase(it);
break;
}
}
}
Although there's no straight-forward method you still can do this (for custom message using reflection). Code below removes count
repeated field items starting from row
index.
void RemoveFromRepeatedField(
const google::protobuf::Reflection *reflection,
const google::protobuf::FieldDescriptor *field,
google::protobuf::Message *message,
int row,
int count)
{
int size = reflection->FieldSize(*message, field);
// shift all remaining elements
for (int i = row; i < size - count; ++i)
reflection->SwapElements(message, field, i, i + count);
// delete elements from reflection
for (int i = 0; i < count; ++i)
reflection->RemoveLast(message, field);
}