Replacing loops with linq code

My current code is as follows:

    var results = new List<Results>();

    var items = new List<string>
    {
        "B,0",
        "A,1",
        "B,2",
        "A,3",
        "A,4",
        "B,5",
        "A,6",
        "A,7",
        "B,8"
    };

    int size = 2;
    int temp;
    var tempResults = new List<int>();

    var keys = items.Select(t => t.Split(',')[0]).Distinct().ToList();
    //var values = items.Select(t => t.Split(',')[1]).ToList();

    //var result = items.SelectMany(k => values, (k, v) => new {k, v});

    foreach (var key in keys)
    {
        temp = 0;
        tempResults = new List<int>();
        foreach (var item in items)
        {
            if (item.Split(',')[0] == key)
            {
                tempResults.Add(Int32.Parse(item.Split(',')[1]));
                temp++;
            }

            if (temp == size)
            {
                results.Add(new Results
                {
                    Key = key,
                    Values = new List<int>(tempResults)
                });
                temp = 0;
                tempResults.Clear();
            }
        }
    }

    foreach (Results r in results)
    {
        Console.WriteLine("Key: " + r.Key);
        Console.WriteLine("Values: ");
        foreach (int i in r.Values)
        {
            Console.WriteLine(i);
        }
    }

Everything works fine, but I use two loops to get the desired results. I want to replace them with a LINQ expression and try, but can't seem to figure it out. Any help is appreciated.

+4
source share
3 answers

You can use a combination of LINQ methods: .GroupBy, .Select, SelectManyand some data structures, such as Tuple<T1, T2>.

Provided that we have a class:

class Results
{
    public string Key { get; set; }
    public List<int> Values { get; set; }
}

The solution may be:

        int k = 0;
        var result =
            items.Select(x =>                          // parse initial string
                 {
                     var strValue = x.Split(',');
                     return Tuple.Create(strValue[0], Convert.ToInt32(strValue[1]));
                 })
                 .GroupBy(x => x.Item1, y => y.Item2)  // group by key
                 .Select(x => Tuple.Create(x.Key, x))  // flatten to IEnumerable
                 .SelectMany(x =>                      // select fixed size data chunks
                     x.Item2.GroupBy(y => k++ / size, z => z)
                            .Select(z => Tuple.Create(x.Item1, z)))
                 .Select(x =>                          // cast to resulting model type
                     new Results()          
                     {
                         Key = x.Item1,
                         Values = x.Item2.ToList()
                     })
                 .ToList();                            // Return enumeration as list
+2
source

It is not possible to remove the inner loop, but you can shorten your code a bit:

....
var keys = items.Select(t => t.Split(',')[0]).Distinct().ToList();

foreach (var key in keys)
{
    var forKey = items.Where(x => x.Split(',')[0] == key)
                      .Select(k => int.Parse(k.Split(',')[1]));
    for (int x = 0; x < forKey.Count(); x += size)
    {
        results.Add(new Results
        {
            Key = key,
            Values = forKey.Skip(x).Take(size).ToList()
        });
    }
}
....

, if A, .

+1

?

const int partitionSize = 2;
var itemLookup = items.ToLookup(x => x.Split(',')[0], x => Int32.Parse(x.Split(',')[1]));
var partitionedItems = itemLookup.Partition(partitionSize);

foreach (var partition in partitionedItems)
foreach (var lookup in partition)
{
    Console.WriteLine("Key: " + lookup.Key);
    Console.WriteLine("Values: ");
    foreach (var i in lookup.ToList())
    {
        Console.WriteLine(i);
    }
}


public static class PartitionExtensions
{
    public static IList<ILookup<K, V>> Partition<K, V>(this ILookup<K, V> lookup, int size)
    {
        return lookup.SelectMany(l => l.ToList().Partition(size).Select(p => p.ToLookup(x => l.Key, x => x))).ToList();
    }

    public static IList<IList<T>> Partition<T>(this IList<T> list, int size)
    {
        IList<IList<T>> results = new List<IList<T>>();
        var itemCount = list.Count();
        var partitionCount = itemCount / size;

        //your paritioning method is truncating items that don't make up a full partition
        //if you want the remaining items in a partial partition, use this code instead
        //var partitionCount = ((itemCount % size == 0) ? itemCount : itemCount + size) / size;

        for (var i = 0; i < partitionCount; i++)
        {
            results.Add(list.Skip(i * size).Take(size).ToList());
        }

        return results;
    }
}
0
source

Source: https://habr.com/ru/post/1692498/


All Articles