Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq query Group By with contains

I have a list of the List of string that is Currency Code.

var currencyCode = new List<string>() { "USD", "SGD", "KWD", "BHD", "LYD" };

And i Have another complex object.

 var rate = new List<Rate>()
 {
        new Rate() { CurrencyName = "USD (SMALL)",CurrencyCode = "USD SMALL",BranchName="Branch1"},
        new Rate() { CurrencyName = "SGD BIG",CurrencyCode = "SGD BIG",BranchName="Branch1"},
        new Rate() { CurrencyName = "KUWAIT DINAR",CurrencyCode = "KWD",BranchName="Branch1"},
        new Rate() { CurrencyName = "USD BIG (100,50)",CurrencyCode = "USD BIG",BranchName="Branch1"},
        new Rate() { CurrencyName = "USD MEDIUM (10,20)",CurrencyCode = "USD MEDIUM",BranchName="Branch1"},
  };

I will have the matched currency in the below list:

var matchedCurrency = from c in rate
                      where currency.Any(w => c.CurrencyCode.Contains(w))
                      select c;

What i wanted is that the matching currency list should be in grouped, grouped by currency code.

I tried by the following way but it did not worked.

 var Grp = rate.GroupBy(item => currency.Any(w => item.CurrencyCode.Contains(w)))
          .Select(group => new
          {
              group.Key,
              DataList = group.ToList()
           });

I don't get i am actually missing. I have tried by various ways.

I know i can loop through the rate and push into another object. But that does not look nice i wanted to do this by using Linq. But i could not achieve the point.

Output will be displayed with this object:

public class CurrencyMap
{
    public string Code { get; set; }

    public List<Currency> currency { get; set; }
}
public class Currency
{
    public string CurrencyName { get; set; }
    public string CurrencyCode { get; set; }
    public string BranchName { get; set; }
}
enter code here

EDIT:

I missed the things at first but i also need to have the empty list if the matching code was not found in the rate.

In Rate there is not the matching list for "BHD", "LYD". But i also need to have the empty list with the code "BHD", "LYD"

like image 445
AkshayKriti Avatar asked Feb 16 '18 06:02

AkshayKriti


Video Answer


2 Answers

First select the matching currency code, then group by the selected code.

var groupedRates = rate
    .Select(r => new
    {
        rate = r,
        code = currencyCode.FirstOrDefault(c => r.CurrencyCode.Contains(c))
    })
    .GroupBy(x => x.code, x => x.rate); //maybe you don't want to throw away the resolved code like I do in the element selector...

Edit: I guess I was a bit to focused on the grouping aspect. Since you want to include all currency codes and mentioned a specific output structure, forget about grouping and just select your result:

var groupedRatesList = currencyCode
    .Select(c => new CurrencyMap
    {
         Code = c,
         currency = rate
            .Where(x => x.CurrencyCode.Contains(c))
            .Select(x => new Currency
            {
                BranchName = x.BranchName,
                CurrencyCode = x.CurrencyCode, // or maybe you want to insert c here?
                CurrencyName = x.CurrencyName
            })
            .ToList()
     })
     .ToList();
like image 101
grek40 Avatar answered Sep 22 '22 06:09

grek40


It is a rather hacky approach but you could use Regex.Match to achieve this. The basic idea is that you need the value from currencyCode as the key for your grouping.

This can be returned by a sucessfull match with regex. The property Match.Value will contain the string value for the key

Disclaimer: Unfortunately all negative matches will be return also as empty groups. You would need to filter then the empty groups out:

var result = currencyCode.SelectMany
             (
                 x=> rate.Where(r=> r.CurrencyCode.Contains(x))
                         .GroupBy(r=> Regex.Match(r.CurrencyCode, x).Value)
             ).Where(x=> !string.IsNullOrWhiteSpace(x.Key));

Actually it works also without regex:

var result = rate.GroupBy(r => currencyCode.FirstOrDefault(c=> r.CurrencyCode.Contains(c)))
                 .Where(x=> !string.IsNullOrWhiteSpace(x.Key));

Disclaimer 2: Like all pattern matching it will lead to problems if you have ambiguous patterns. If a CurrencyCode value contains more than 1 of the abriviations ( may be inside the word ) you can get non sensical results/ or double entries.

Although I found it to be an intriguing problem to solve with linq, personally I would refrain from this approach. If I would have to return to this code after 9 months to maintain it, I would be way more happy to read this:

Dictionary<string,IEnumerable<Rate>> groupedSet = new Dictionary<string, IEnumerable<Rate>>();

foreach (var key in currencyCode)
{
    IEnumerable<Rate> _result = rate.Where(x => x.CurrencyCode.Contains(key));
    if (_result.Any())
    {
        groupedSet.Add(key, _result);           
    }
}

than to start remembering what da hack I wrote back then and what I might have thought of when I wrote it back then....

like image 38
Mong Zhu Avatar answered Sep 22 '22 06:09

Mong Zhu