Intersection of Lists

  • Thread starter Thread starter Shapper
  • Start date Start date
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
 
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
 
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
 
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
 
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
 
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
 
Back
Top