Search code examples
c#linqiequalitycomparer

Group linq results by value and group null or invalid values by empty string


I am trying to group by a partial zip code and if any are zip codes that are null or less than 3 characters group them as ""

I've seen some example of using a nullable comparer but not sure of how to fit something like that in the syntax in the context of below.

Also the QBModel.ResultsTable is a dynamic list and the CallerZipCode is a char(10) so something with the valid value, could be "96701-----"

  var newset = (from rst in QBModel.ResultsTable
          group rst by rst.CallerZipCode.Substring(0, 3) into newGroup
          select new DataSourceRecord()
          {
            State = ToTitleCase(newGroup.Select(i => i.CallerState).FirstOrDefault()),
            ZipCode = newGroup.Where(z => z.CallerZipCode.StartsWith(newGroup.Key)).Select(x => x.CallerZipCode.Substring(0, 3)).FirstOrDefault()
          }).ToList();

Here is a nullable comparer I found but probably needs work if I'm going to check for zip codes less than 2 characters:

public class NullableComparer<T> : IEqualityComparer<T?> where T : struct
{
    public bool Equals(T? x, T? y)
    {
        if (x == null || y == null)
            return false;
        return x.Equals(y);
    }

    public int GetHashCode(T? obj)
    {
        return obj.GetHashCode();
    }
}

How can I change this code to accomplish what I am after?

[Edit]

Just tried something like this, but it didn't seem to work very well

  var newset = (from rst in QBModel.ResultsTable
          group rst by rst.CallerZipCode == null || rst.CallerZipCode.Trim().Length() < 3 ? "<null>" : rst.CallerZipCode.Substring(0, 3) into newGroup
          select new DataSourceRecord()
          {
            State = ToTitleCase(newGroup.Select(i => i.CallerState).FirstOrDefault()),
            ZipCode = newGroup.Where(z => z.CallerZipCode.StartsWith(newGroup.Key)).Select(x => x.CallerZipCode.Substring(0, 3)).FirstOrDefault()
          }).ToList();

Solution

  • I don't think that I have completely understood what you are after, but here is my take:

    You want to group by the zip code, but if the zip code is null or empty or less than 3 characters long, you want to put them in the group "<null>".

    If that is what you want, you could try something like the following:

      var newset = (from rst in QBModel.ResultsTable
              group rst by GetGroupRepresentation(rst.CallerZipCode) into newGroup
              select new DataSourceRecord()
              {
                // ...
              }).ToList();
    

    With the following implementation for GetGroupRepresentation:

    private string GetGroupRepresentation(string zipCode)
    {
        if (string.IsNullOrEmpty(zipCode) || zipCode.Length < 3)
        {
            return "<null>";
        }
    
        return zipCode;
    }
    

    I didn't understand why you are using the Substring-method or StartsWith-method for, so I just removed it.

    Here is a full example:

    static void Main(string[] args)
    {
        var zipcodes = new List<string> { "1234", "4321", null, "", "12" };
    
        // LINQ Query Syntax
        var groups = from code in zipcodes
                     group code by GetGroupRepresentation(code) into formattedCode
                     select formattedCode;
    
        // I think this is easier to read in LINQ Method Syntax.
        // var groups = zipcodes.GroupBy(code => GetGroupRepresentation(code));
    }
    
    private static string GetGroupRepresentation(string zipCode)
    {
        if (string.IsNullOrEmpty(zipCode) || zipCode.Length < 3)
        {
            return "<null>";
        }
    
        return zipCode;
    }
    

    enter image description here