# Maintaining a single collection subdivided by a value's property

I'm working on a networked game. In this game, the base entity is called an actor. An actor can either be replicated or local, with each instance having a "replicates" field that represents this.

When managing a scene, a collection of actor must be maintained. My problem is finding a suitable solution to this. Right now I store a collection of references to local actors, replicated actors and all actors. This means data is duplicated which results in code like this:

        for( int i = 0; i < actorsToRemove.Count; ++i )
{
if( actorsToRemove[i].Replicates )
{
replicatedActors.Remove( actorsToRemove[i].Id );
}
else
{
localActors.Remove( actorsToRemove[i] );
}
allActors.Remove( actorsToRemove[i] );
}
actorsToRemove.Clear();


Instead of just removing from a single collection.

I could have one single collection that holds all actors and use properties that return another collection. For example:

public List<Actor> ReplicatedActors
{
get
{
// loop through all actors and return a new list that holds all replicating actors.
}
}


But this seems like too much unnecessary copying.

What you suggested is absolutely fine. There isn't any unnecessary copying. Although, to make things simpler, I suggest using LINQ extension methods for filtering.

using System.Linq;

public IEnumerable<Actor> ReplicatedActors
{
get { return allActors.Where(actor => actor.Replicates); }
}

public IEnumerable<Actor> LocalActors
{
get { return allActors.Where(actor => !actor.Replicates); }
}


You'll still have to remove actors from allActors, these two are separate read-only lists of actors derived from the list of all actors.

Edit: I also suggest changing your actor removal loop to be a foreach loop for simplicity.

foreach (var actor in actorsToRemove)
{
allActors.Remove(actor);
}
actorsToRemove.Clear();

• But those properties return an entirely new list with all the references copied over, right? Do you think the copying is negligible? – Ben Jun 25 '15 at 6:19
• Yes, they are entirely new lists. Both properties use IEnumerable as return types, so they're lazily evaluated. The copying doesn't matter unless allActors is massive in size. Microsoft's implementation of the CLR has a maximum of 2GB for object sizes. For most lists, that means hundreds of millions of items, if not billions. If you run into problems with the list size, you should look into ways of reducing it or caching ReplicatedActors and LocalActors. – Spans Jun 25 '15 at 6:29
• Do you suggest LINQ for any reason other than it's conciseness? – Ben Jun 25 '15 at 7:14
• It's powerful, it's simple, it's lazy, and these are just the extension methods. Actual LINQ is all those and even more. Here's a great resource on getting started with LINQ. – Spans Jun 25 '15 at 7:51

If you have three lists (localActors, replicatedActors, allActors) and you want to remove from them elements from actorsToRemove (with corresponding flag), you can utilise .Except method:

localActors = localActors.Except(actorsToRemove.Where(a => !a.Replicates).ToList();
replicatedActors = replicatedActors.Except(actorsToRemove.Where(a => a.Replicates).ToList();
allActors = allActors.Except(actorsToRemove).ToList();
actorsToRemove.Clear();


The solution with properties doesn't do exactly that. Using properties has the advantage that you can avoid storing data in three separate collections. It'll require using those properties inside a single class, and will not work for random lists.

Edit: My original idea with .RemoveRange was totally wrong, sorry. .Except should work with any IEnumerable<> though. It looks a bit noisy to my liking though :(.