8

I am sorting a list of elements:

var matchEle = listOfElements.Where(e => e.Properties().Any(p => p.Name.Contains("Key", Asking for IEqualityComparer))).First();

I am used to just going straight to a StringComparer, OrdinalIgnoreCase or CurrentCultureIgnoreCase, however when calling Contains() in this context, it is asking for an IEqualityComparer. I imagine because of the data structure/level. I saw an example of how to set up an IEqualityComparer such as

strEqualityComparer = new IEqualityComparer();

and defining the class for strEqualityComparer but I am not sure beyond that. Can someone help me get my linq statement to work with an ignore case?

Update: Just so I'm clear here is an example of the data structure:

listOfElements = [element1, element2, etc..]
element1.Properties = ["Prop1", "Key1", "Prop2", "Key2", etc.]

I need to extract the elements which pass the filter if any of its properties has a value containing the keyword, in this case "Key" therefore it cannot be .Equals or IndexOf.

AndyBernard
  • 105
  • 1
  • 9
  • if ``Name`` is a ``string``, you don't need to use ``contains`` just ``Equals`` and ignore case. but if you change your query to :``(e => e.Properties().Select(p => p.Name).Contains("Key"))`` you need here to use a custom comparer – Mohammed Sajid Apr 12 '20 at 19:07
  • @GertArnold I believe .Net 4.8 – AndyBernard Apr 12 '20 at 23:53
  • Then there is no `Contains` method that accepts a `string` and an `IEqualityComparer`. For some odd reason there's only an overload with `char` + `IEqualityComparer` (because it's based on `IEnumerable`). – Gert Arnold Apr 13 '20 at 07:34
  • Making your question a duplicate of this one: https://stackoverflow.com/q/444798/861716. – Gert Arnold Apr 13 '20 at 07:39
  • Hey @GertArnold I will have to say this is the first time I've seen IndexOf used as a Contains function. I am a bit confused now. I always thought IndexOf was looking for the index of the exact item but in the example you referenced as well as in example by Sajid IndexOf is being used to find the index of an item that contains the provided key. So fundamentally, I guess my question is, is IndexOf() then a return of the first occurrence of an item that contains the provided key vs equals the provided key? – AndyBernard Apr 13 '20 at 17:18

1 Answers1

10

Update as per comment

Search string inside another string:

var matchEle = listOfElements
.Where(e => e.Properties().Any(p => p.Name.IndexOf("Key", System.StringComparison.OrdinalIgnoreCase) >= 0))
.First();

Old solutions

You have two options, that depends on Name type:
1 - Without IEqualityComparer, and if Name in Properties is a string. replace Contains by Equals like :

var matchEle = listOfElements
    .Where(e => e.Properties().Any(p => p.Name.Equals("Key", StringComparison.OrdinalIgnoreCase)))
    .First();

2 - With IEqualityComparer, and if Name in Properties is a list of string:
2.1 : Create a custom comparer, like:

public class StringIEqualityComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.Equals(y, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(string obj)
    {
        return obj.GetHashCode();
    }
}

2.2 : change little your query to :

var matchEle = listOfElements
.Where(e => e.Properties().Any(p => p.Name.Contains("Key", new StringIEqualityComparer())))
.First();

I hope this helps you.

Mohammed Sajid
  • 4,778
  • 2
  • 15
  • 20
  • Hey @Sajid Thanks for the response. I did actually setup my own IEqualityComparer in the same manner as you've written prior to this post. However when I use it, Visual Studio throws an Exception, NullException, No overload method 'Contains' takes 2 arguments even though via intellisense it shows the option to add an IEqualityComparer. – AndyBernard Apr 12 '20 at 22:06
  • I also cannot use Equals because I am unfortunately not providing an exact key to match I need to cull out certain strings that contain the keyword. – AndyBernard Apr 12 '20 at 22:09
  • if i understand, you search a 3d options, search some string inside a string? – Mohammed Sajid Apr 12 '20 at 22:12
  • @AndyBernard : i have tested the 3d option, it's wok well. by using just ``indexOf`` to search ``key`` inside ``Name``. can you check it. – Mohammed Sajid Apr 12 '20 at 22:26
  • @AndyBernard : for the first comment, it's normal, because the proposed comparer is for ``char`` not for ``string``. – Mohammed Sajid Apr 12 '20 at 22:50
  • but wouldn't IndexOf still have to match the keyword exactly? It needs to work like Contains that way something like Key 1 or Key A can pass and not just the exact word Key – AndyBernard Apr 12 '20 at 23:51
  • Yes, exactly, its' work if you have ``Key 1`` and ``Key A`` in ``properties`` and you search matches ``key``. ``indexOf`` will gives ``Key 1`` and ``Key A``. – Mohammed Sajid Apr 13 '20 at 07:44
  • @AndyBernard have you tested the last update? – Mohammed Sajid Apr 13 '20 at 21:09
  • Hi @Sajid I think it works but I do have a question about it. Can you explain why IndexOf() works this way here? I always thought IndexOf() returned the first occurrence of the EXACT match of a key but here it is providing the first occurrence of an item that CONTAINS the key. – AndyBernard Apr 13 '20 at 21:36
  • IndexOf : Reports the *zero-based index* of the first occurrence of the specified *string* in the current System.String object. and Returns : The index position of the value parameter if that string is found, or -1. so if the key match, the index will be >=0, not match -1. – Mohammed Sajid Apr 13 '20 at 21:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/211583/discussion-between-sajid-and-andybernard). – Mohammed Sajid Apr 13 '20 at 21:43
  • 2
    Note that this implementation violates the contract for using in a HashSet as equal objects will not always return equal hash codes. One suboptimal fix would be to do a `ToLower` on `obj` inGetHashCode so that case insensitivity is taken into account. For the OP use case this is not a concern but a note for others if trying to use this in the general case. – GameSalutes Nov 04 '21 at 02:09
  • A slight modification to the comparer in the old solution to allow for comparison specified in the new () could be ```public class StringIEqualityComparer : IEqualityComparer { private StringComparison Comparison = StringComparison.Ordinal; public StringIEqualityComparer(StringComparison comparison) { Comparison = comparison; } public bool Equals(string x, string y) { return x.Equals(y, Comparison); } }``` – J Man May 11 '23 at 22:30
  • Then you can call it like ```var matchEle = listOfElements .Where(e => e.Properties().Any(p => p.Name.Contains("Key", new StringIEqualityComparer(StringComparison.OrdinalIgnoreCase)))) .First();``` – J Man May 11 '23 at 22:31