Intersection of Lists

S

Shapper

Hello,

I have a class as follows:

public class User {

public Int32 Id { get; set; }
public IList<Role> Roles { get; set; }

} // User

The Role class as a property NAME. I have the following string array:

String[] filterRoles = { "role2", "role4 };

I need to select all users which roles contains at least of of these roles.

How can I do this?

Thank You,

Miguel
 
A

Arne Vajhøj

I have a class as follows:

public class User {

public Int32 Id { get; set; }
public IList<Role> Roles { get; set; }

} // User

The Role class as a property NAME. I have the following string array:

String[] filterRoles = { "role2", "role4 };

I need to select all users which roles contains at least of of these roles.

How can I do this?

One possibility:

using System;
using System.Collections.Generic;
using System.Linq;

namespace E
{
public class Role
{
public string Name { get; set; }
}
public class User
{
public int Id { get; set; }
public IList<Role> Roles { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
IList<User> users = new List<User>();
users.Add(new User { Id=1, Roles=new List<Role>() });
users[0].Roles.Add(new Role { Name="role1" } );
users[0].Roles.Add(new Role { Name="role2" } );
users.Add(new User { Id=2, Roles=new List<Role>() });
users[1].Roles.Add(new Role { Name="role3" } );
users.Add(new User { Id=3, Roles=new List<Role>() });
users[2].Roles.Add(new Role { Name="role4" } );
users.Add(new User { Id=4, Roles=new List<Role>() });
string[] filter = { "role2", "role4" };
IList<User> filtered = users.Where(u => u.Roles.Select(r =>
r.Name).Intersect(filter).Any()).ToList();
foreach(User u in filtered)
{
Console.WriteLine(u.Id);
}
Console.ReadKey();
}
}
}

Arne
 
A

Arne Vajhøj

I have a class as follows:

public class User {

public Int32 Id { get; set; }
public IList<Role> Roles { get; set; }

} // User

The Role class as a property NAME. I have the following string array:

String[] filterRoles = { "role2", "role4 };

I need to select all users which roles contains at least of of these
roles.

How can I do this?

One possibility:

using System;
using System.Collections.Generic;
using System.Linq;

namespace E
{
public class Role
{
public string Name { get; set; }
}
public class User
{
public int Id { get; set; }
public IList<Role> Roles { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
IList<User> users = new List<User>();
users.Add(new User { Id=1, Roles=new List<Role>() });
users[0].Roles.Add(new Role { Name="role1" } );
users[0].Roles.Add(new Role { Name="role2" } );
users.Add(new User { Id=2, Roles=new List<Role>() });
users[1].Roles.Add(new Role { Name="role3" } );
users.Add(new User { Id=3, Roles=new List<Role>() });
users[2].Roles.Add(new Role { Name="role4" } );
users.Add(new User { Id=4, Roles=new List<Role>() });
string[] filter = { "role2", "role4" };
IList<User> filtered = users.Where(u => u.Roles.Select(r =>
r.Name).Intersect(filter).Any()).ToList();
foreach(User u in filtered)
{
Console.WriteLine(u.Id);
}
Console.ReadKey();
}
}
}

NB: I believe that the design could be improved a bit by
encapsulating better.

Arne
 
M

Marcel Müller

Hello,

I have a class as follows:

public class User {

public Int32 Id { get; set; }
public IList<Role> Roles { get; set; }

} // User

The Role class as a property NAME. I have the following string array:

String[] filterRoles = { "role2", "role4 };

I need to select all users which roles contains at least of of these roles.

How can I do this?

Arne already gave you an example. But if you do this quite often, you
might prefer other implementations, because Intersect is quite slow. (It
creates a temporary hash table on each invocation.)

The first improvement could be, that you use a sorted list or a
dictionary for Roles, using NAME as a key. (This requires that Role.NAME
is immutable.)
public IDictionary<string,Role> Roles { get; set; }
Now you can define a extension function
public static bool IsInRoles(this IDictionary<string,Role> roles,
params string[] names)
{ return names.Any(name => roles.ContainsKey(name));
}
Using a class Roles that inherits from IDictionary<string,Role> makes
things even more readable.

If the set of roles that your application can handle is limited, then
you might further use a flags enum. This is extremely fast and memory
conserving.
[Flags]
enum Roles
{ none = 0
role1 = 1 << 0,
role2 = 1 << 1,
role3 = 1 << 2,
role4 = 1 << 3,
...
}
Now your Roles property is of type Roles rather than IList<...>.
public Roles Roles { get; set; }

Checking for a permission is now a very simple bit operation. You could
again use a extension method.
public static bool IsInRoles(this Roles roles1, Roles roles2)
{ return (roles1 & roles2) != 0;
}

The drawback is, that you need to convert the strings to the enum type
once. But if the naming is consistent with the enum identifiers this is
quite easy.
public static Roles ParseRoles(string[] names)
{ return names.Aggregate(Roles.none, (roles,name) =>
roles | (Roles)Enum.Parse(typeof(Roles), name, true));
}


Marcel
 
M

Michael Böhnisch

On Friday, July 13, 2012 11:10:10 PM UTC+2, Arne Vajhøj wrote:
{..]
IList&lt;User&gt; filtered = users.Where(u =&gt; u.Roles..Select(r =&gt;
r.Name).Intersect(filter).Any()).ToList();

Just to throw in another approach without Intersect() ... no idea if this is any improvement in LINQ to Objects:

IList<User> filtered = (
from User u in users where (
from Role r in u.Roles
join string fr in filter on r.Name equals fr
select r
).Count() > 0
select u
).ToList();

Michael
 
A

Arne Vajhøj

On Friday, July 13, 2012 11:10:10 PM UTC+2, Arne Vajhøj wrote:
{..]
IList&lt;User&gt; filtered = users.Where(u =&gt; u.Roles.Select(r =&gt;
r.Name).Intersect(filter).Any()).ToList();

Just to throw in another approach without Intersect() ... no idea if this is any improvement in LINQ to Objects:

IList<User> filtered = (
from User u in users where (
from Role r in u.Roles
join string fr in filter on r.Name equals fr
select r
).Count() > 0
select u
).ToList();

I don't know how it performs. If it materializes for each user when
doing Count then it may be expensive.

For data sizes expected for users/roles then it should not
matter.

And then it becomes a matter of readability.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

NHibernate QueryOver with Many-to-Many 0
Generics 7
Linq query 3
List<String> 15
Update Object 5
List. Add, Remove. 1
Add item at start 5
Sring[] to List<MyEnumeration> 5

Top