# Efficient use of LINQ - Indexing

I have a Class with a List of objects, and those objects contain a list which has lists to objects in the first class, and I'm trying to quickly index them. Simplified:

class Person
{
public IEnumberable<HistoricalEvent> Events
{
get
{
return World.HistoricalEvents.Values.Where(x=>x.PeopleInvolved.Contains(this));
}
}
}


Also

class HistoricalEvent
{
public virtual IEnumerable<HistoricalFigure> PeopleInvolved
{
get { return Enumerable.Empty<People>(); }
}
...
}


and a subclass

class HistoricalEventSubclass : HistoricalEvent
{
Person person1 = new Person("Alice");
Person person2 = new Person("Bob");

public override IEnumerable<Person> PeopleInvolved
{
get
{
yield return person1;
yield return person2;
}
}
...
}


The Events class is called from a method using: frm.grpHistoricalFigureEvents.FillListboxWith(frm.lstHistoricalFigureEvents, Events);

    internal static void FillListboxWith(this GroupBox groupbox, ListBox listbox, IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
{
groupbox.Visible = false;
return;
}
groupbox.Visible = true;
listbox.BeginUpdate();
listbox.Items.Clear();
listbox.EndUpdate();
listbox.SelectedIndex = 0;
var title = groupbox.Text.Split('(')[0].Trim();
groupbox.Text = string.Format("{0} ({1})", title, listbox.Items.Count);
}


The problem is that calling Events takes a long time (as World.HistoricalEvents can be a very big List). Is there a more time-efficient way of doing this?

Part of the slowdown is that Events is converted ToArray() and shoved into a listbox, and so I'll also be pursuing an alternative such as a listbox which can handle an IEnumberable directly.

• If there is more code, please provide that. Unlike SO, we don't work with simplified code. – Jamal Mar 17 '15 at 16:56
• You should profile it to see where the real slowdowns are, but my gut tells me a big win could be had by implementing Equals and GetHashCode on Person and changing PeopleInvoled to be ISet<T>. Then, use a HashSet<T> for the underlying implementation. This makes more sense from a domain perspective, as a person's involvement with an event is binary - it makes no sense that they would appear twice. – Dan Lyons Mar 17 '15 at 17:11
• You are correct that ToArray() is a likely slowdown. Ditto if you use ToList(). If you have 100K events, your code is held up until all 100K events are collected, despite the use of yield. Consider using a BindSource.DataSource, which accepts an IEnumerable collection. – Rick Davin Mar 17 '15 at 17:32
• @Jamal, Added the last step in the chain of this, there is a ton more code and I don't want to get bogged down in unrelated issues otherwise though. – Mason11987 Mar 17 '15 at 20:27
• @RickDavin, as far as I can tell a BindingSource.DataSource isn't polymorphic, and the Events Method returns a set of Events which may be of many subclasses, only one was shown, so it doesn't work in those cases. Is there an alternative that is polymorphic or am I using it wrong? – Mason11987 Mar 17 '15 at 20:27

How do you load the data for events and people? If you can denormalise your data so that a person has a materialised Set/List of events they are involved in I suspect you would gain a lot of performance (at the cost of a bit of memory).

Here is some pseudo code to explain what I mean:

var personStore = new Dictionary<string, Person>();
var eventStore = new Dictionary<string, Event>();
foreach (var eventData in eventSource)
{
var event = new Event(eventData);
eventStore[eventData.Name] = event;

foreach (var personData in eventData)
{
// Is this the first time we've seen this person?
{
personStore[personData.Name] = new Person(personData);
}

var person = personStore[personData.Name];

// This creates the bidirectional mapping which makes lookups
// in either direction fast.
}
}

• Great question, that is essentially the set up I had prior to attempting this alternative. If I couldn't find a good enough improvement I was planning on rolling back to this alternative. The problem is that each of the event subclasses handled associated people differently and so this option allowed me to drop a huge amount of custom association code in 30+ classes, which was a big win. Lots of balancing factors. – Mason11987 Mar 18 '15 at 19:47