1

I have following code that checks whether userRoles collection has any of the values in authorizedRolesList. It does not work if the userRoleName has a whitespace.

What is the most efficient LINQ way for handling this?

CODE

        List<string> authorizedRolesList = null;
        string AuthorizedRolesValues = "A, B ,C,D";
        if (!String.IsNullOrEmpty(AuthorizedRolesValues))
        {
            authorizedRolesList = new List<string>((AuthorizedRolesValues).Split(','));
        }

        string userRoleName = String.Empty;

        Collection<string> userRoles = new Collection<string>();
        userRoles.Add("B   ");

        bool isAuthorizedRole = false;
        if (userRoles != null)
        {
            foreach (string roleName in userRoles)
            {
                userRoleName = roleName.Trim();
                if (authorizedRolesList != null)
                {
                    //Contains Check
                    if (authorizedRolesList.Contains(userRoleName))
                    {
                        isAuthorizedRole = true;
                    }
                }

            }
        }

REFERENCE:

  1. When to use .First and when to use .FirstOrDefault with LINQ?
  2. Intersect with a custom IEqualityComparer using Linq
  3. Ignoring hyphen in case insensitive dictionary keys
  4. C#: splitting a string and not returning empty string
  5. When does IEnumerable.Any(Func) return a value?
  6. Is IEnumerable.Any faster than a for loop with a break?
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • 1
    use Enumerable.Any Method – skjcyber Dec 06 '12 at 12:44
  • The answers below pretty much cover it, however, if you have whitespace in the middle of a string (like "Role A") then .Trim won't work. It only removes whitespace from the beginning and end of a string. You can use String.Replace(" ", "") to handle that scenario. – Forty-Two Dec 06 '12 at 12:54

6 Answers6

4

I guess most efficient LINQ way means most readable here.

The obvious way is to use StringSplitOptions.RemoveEmptyEntries when calling Split() and not storing the whitespace in the first place.

authorizedRolesList = AuthorizedRolesValues.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries);

But if for some reason you want to keep the additional whitespace or can't change the entries in authorizedRolesList, you can easily change your if clause from

if (authorizedRolesList.Contains(userRoleName))

to

if (authorizedRolesList.Any(x => x.Trim() == userRoleName))

BTW, talking about LINQ:

You could just replace your code with

bool isAuthorizedRole = userRoles.Any(ur => authorizedRolesList.Any(ar => ar.Trim() == ur.Trim()))

if you ensure userRoles and authorizedRolesList are not null (use an empty collection instead).

Even more readable IMHO would be something like

bool isAuthorizedRole = userRoles.Intersect(authorizedRolesList, new IgnoreWhitespaceStringComparer()).Any();

where IgnoreWhitespaceStringComparer would look like

class IgnoreWhitespaceStringComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.Trim().Equals(y.Trim());
    }

    public int GetHashCode(string obj)
    {
        return obj.Trim().GetHashCode();
    }
}
sloth
  • 99,095
  • 21
  • 171
  • 219
  • Thanks. Does "Any" iterate through all the elements in intersect result? Or does it only check whether there exist a value? [ I am referring to the `IEqualityComparer` approach] – LCJ Dec 06 '12 at 13:57
  • 1
    `Any` iterates through the collection, but stops iterating when there's an element that satisfies the condition. So in the best case it has only to check the first element; in the worst case it has to check all elements. – sloth Dec 06 '12 at 14:04
  • In fact, I have one more requirement. I need to get the first matching roleName. Is there a way to get it without storing the `intersect` result in a variable? – LCJ Dec 06 '12 at 14:37
  • 1
    Then I suggest you use `userRoles.Intersect(authorizedRolesList, new IgnoreWhitespaceStringComparer()).FirstOrDefault()`. This will return the first matching rolename if any, and `null` when there's no match. – sloth Dec 06 '12 at 14:44
1

just try like this remove whitespace when splitting string and use Trim()

 List<string> authorizedRolesList = null;
    string AuthorizedRolesValues = "A, B ,C,D";
    if (!String.IsNullOrEmpty(AuthorizedRolesValues))
    {
        string[] separators = {","};
        authorizedRolesList = new List<string>(
           ((AuthorizedRolesValues)
                  .Split(separators , StringSplitOptions.RemoveEmptyEntries))
                  .Select(x => x.Trim());
    }

and after this do use Trim() like this in below code

                //Contains Check
                if (authorizedRolesList.Contains(userRoleName.Trim()))
                {
                    isAuthorizedRole = true;
                }
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
1

Just trim every entry in your list like so:

authorizedRolesList.ForEach(a => a = a.Trim());
DerApe
  • 3,097
  • 2
  • 35
  • 55
1
        string authorizedRolesValues = "A, B ,C,D";

        var authorizedRolesList = authorizedRolesValues
            .Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
            .Select(role => role.Trim());

        var userRoles = new Collection<string> {"B   "};

        bool isAuthorizedRole = userRoles
            .Select(roleName => roleName.Trim())
            .Any(authorizedRolesList.Contains);
ABosyy
  • 11
  • 1
0

How about trimming the original list also?

authorizedRolesList = new List<string>((AuthorizedRolesValues).Split(',').Select(x => x.Trim()));

nakiya
  • 14,063
  • 21
  • 79
  • 118
0

try this

bool ifExists = userRoles.Any(AuthorizedRolesValues.Split(',').Select(val => val = val.trim());
skjcyber
  • 5,759
  • 12
  • 40
  • 60