Steven C. Britton
Steven C. Britton

Reputation: 482

How do I use Linq with a HashSet of Integers to pull multiple items from a list of Objects?

I have a HashSet of ID numbers, stored as integers:

HashSet<int> IDList; // Assume that this is created with a new statement in the constructor.

I have a SortedList of objects, indexed by the integers found in the HashSet:

SortedList<int,myClass> masterListOfMyClass;

I want to use the HashSet to create a List as a subset of the masterListOfMyclass.

After wasting all day trying to figure out the Linq query, I eventually gave up and wrote the following, which works:

public List<myclass> SubSet {

        get {
            List<myClass> xList = new List<myClass>();

            foreach (int x in IDList) {
                if (masterListOfMyClass.ContainsKey(x)) {

                    xList.Add(masterListOfMyClass[x]);
                }
            }

            return xList;
        }

        private set { }
    }

So, I have two questions here:

  1. What is the appropriate Linq query? I'm finding Linq extremely frustrating to try to figuere out. Just when I think I've got it, it turns around and "goes on strike".

  2. Is a Linq query any better -- or worse -- than what I have written here?

Upvotes: 0

Views: 688

Answers (4)

alzaimar
alzaimar

Reputation: 4622

Another option would be (which is intentionally the same as Sankarann's first answer)

return (
   from x in IDList 
   where masterListOfMyClass.ContainsKey(x) 
   select masterListOfMyClass[x]
   ).ToList();

However, are you sure you want a List to be returned? Usually, when working with IEnumerable<> you should chain your calls using IEnumerable<> until the point where you actually need the data. There you can decide to e.g. loop once (use the iterator) or actually pull the data in some sort of cache using the ToList(), ToArray() etc. methods.

Also, exposing a List<> to the public implies that modifying this list has an impact on the calling class. I would leave it to the user of the property to decide to make a local copy or continue using the IEnumerable<>. Second, as your private setter is empty, setting the 'SubSet' has no impact on the functionality. This again is confusing and I would avoid it.

An alternate (an maybe less confusing) declaration of your property might look like this

public IEnumerable<myclass> SubSet {
    get {
        return from x in IDList 
                where masterListOfMyClass.ContainsKey(x)
                select masterListOfMyClass[x]
    }
}

Upvotes: 1

Jack
Jack

Reputation: 1084

var xList = IDList
    .Where(masterListOfMyClass.ContainsKey)
    .Select(x => masterListOfMyClass[x])
    .ToList();

If your lists both have equally large numbers of items, you may wish to consider inverting the query (i.e. iterate through masterListOfMyClass and query IDList) since a HashSet is faster for random queries.

Edit:

It's less neat, but you could save a lookup into masterListOfMyClass with the following query, which would be a bit faster:

var xList = IDList
    .Select(x => { myClass y; masterListOfMyClass.TryGetValue(x, out y); return y; })
    .Where(x => x != null)
    .ToList();

Upvotes: 3

H&#229;kan Fahlstedt
H&#229;kan Fahlstedt

Reputation: 2095

Here is the Linq expression:

List<myClass> xList = masterListOfMyClass
   .Where(x => IDList.Contains(x.Key))
   .Select(x => x.Value).ToList();

There is no big difference in the performance in such a small example, Linq is slower in general, it actually uses iterations under the hood too. The thing you get with ling is, imho, clearer code and the execution is defered until it is needed. Not i my example though, when I call .ToList().

Upvotes: 1

Sankarann
Sankarann

Reputation: 2665

foreach (int x in IDList.Where(x => masterListOfMyClass.ContainsKey(x)))
{
    xList.Add(masterListOfMyClass[x]);
}

This is the appropriate linq query for your loop.

Here the linq query will not effective in my point of view..

Upvotes: 1

Related Questions