3
\$\begingroup\$

I am tracking trains and trying to identify individual trains seen multiple times at different points through the IDs of the wagons on them when spotted.

// create a lookup of all tracked trains for each wagon
IEnumerable<Train> trains = GetTrains();
var wagonTrains = new Dictionary<int, List<Train>>();
foreach (Train t in trains)
{
 foreach (int w in t.WagonsInTrain)
 {
 if (!wagonTrains.ContainsKey(w))
 {
 wagonTrains.Add(w, new List<Train>());
 }
 wagonTrains[w].Add(t);
 }
}

Is there a better way do to what I am doing in this code segment? Perhaps some chained linq operations? Is there a particular name for the kind of operation I am using here?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jul 25, 2013 at 10:18
\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

create a lookup of all tracked trains for each wagon

That's pretty much what the ToLookup() method is for. You just need a bit more LINQ to get a collection of (train, wagon) pairs, so that ToLookup() can work:

var wagonTrains =
 (from train in trains
 from wagon in train.WagonsInTrain
 select new { train, wagon })
 .ToLookup(x => x.wagon, x => x.train);
answered Jul 25, 2013 at 11:06
\$\endgroup\$
2
\$\begingroup\$

Here is one that uses chained linq operations:

var trainsByWagon =
 trains
 .SelectMany(train => train.WagonsInTrain, (train, wagon) => new { train, wagon })
 .GroupBy(trainAndWagon => trainAndWagon.wagon, trainAndWagon => trainAndWagon.train);
 .ToDictionary(g => g.Key, g => g.ToList());

Small print: Haven't actually tried this, but should work.

answered Jul 25, 2013 at 11:19
\$\endgroup\$
1
  • \$\begingroup\$ Sorry, I eventually accepted the chronologicaly first answer, but both are super useful. \$\endgroup\$ Commented Jul 25, 2013 at 15:37
0
\$\begingroup\$

I've actually set this up in my code as a pair of extension methods:

public static class IDictionaryExtensions
{
 public static void Update<TKEY, TVALUE>(this IDictionary<TKEY, TVALUE> dictionary, TKEY key, TVALUE value)
 {
 if (dictionary.ContainsKey(key))
 {
 dictionary[key] = value;
 }
 else
 {
 dictionary.Add(key, value);
 }
 }
 public static void AddToList<TKEY, TVALUE>(this IDictionary<TKEY, IList<TVALUE>> dictionary, TKEY key, TVALUE value)
 {
 if (dictionary.ContainsKey(key) && dictionary[key] != null)
 {
 dictionary[key].Add(value);
 }
 else
 {
 dictionary.Update(key, new List<TVALUE> {value});
 }
 }
 }

The first method is an all-purpose "Add"-type method which will either add it or update it depending on whether or not it exists. The second is specifically for dictionaries where the value is a List<>. It'll let you add an element to the List<>, creating the key if neccesary.


For your example, usage would be:

IEnumerable<Train> trains = GetTrains();
var wagonTrains = new Dictionary<int, List<Train>>();
foreach (var train in trains)
{
 foreach (var wagon in train.WagonsInTrain)
 {
 wagonTrains.AddToList(wagon, train);
 }
}
answered Jul 25, 2013 at 15:38
\$\endgroup\$
2
  • \$\begingroup\$ Your code wouldn't compile, AddToList() has two parameters. \$\endgroup\$ Commented Jul 25, 2013 at 17:49
  • \$\begingroup\$ @svick - Good catch. That's what I get for freehanding it. Fixed. \$\endgroup\$ Commented Jul 26, 2013 at 13:58

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.