Neicy
Neicy

Reputation: 9

C# Retrieving the absolute value closest to 0 from a sum in a list

What I'm trying to do is get the Time where Math.Abs(A + B + C) is closest to 0 for each ID. It's quite hefty. I have a list (that I got from a CSV file) that kind of looks like this:

        |------------|------------|-------|-------|-------|
        |    Time    |     ID     |   A   |   B   |   C   |
        |------------|------------|-------|-------|-------|
        |    100     |     1      |   1   |   2   |   2   |
        |------------|------------|-------|-------|-------| 
        |    100     |     2      |   3   |   4   |   3   |
        |------------|------------|-------|-------|-------| 
        |    200     |     1      |   1   |   0   |   3   |
        |------------|------------|-------|-------|-------| 
        |    200     |     2      |   1   |   2   |   0   |
        |------------|------------|-------|-------|-------| 

I have the following code, and while it it not complete yet, it technically prints the value that I want. However when I debug it, it doesn't seem to be looping through the IDs, but does it all in one loop. My idea was that I could get all the distinct IDs, then go through the distinct Time for each, put them all in a temporary list, then just use Aggregate to get the value closest to 0. But I feel there should be a more efficient approach than this. Is there a quicker LINQ function I can use to achieve what I want?

for (int i = 0; i < ExcelRecords.Select(x => x.Id).Distinct().Count(); i++)
        {
            for (int j = 0; j < ExcelRecords.Select(y => y.Time).Distinct().Count(); j++)
            {
                List<double> test = new List<double>();

                var a = ExcelRecords[j].a;
                var b = ExcelRecords[j].b;
                var c = ExcelRecords[j].c;
                test.Add(Math.Abs(pitch + yaw + roll));

                Console.WriteLine(Math.Abs(pitch + yaw + roll));
            }
        }

Upvotes: 0

Views: 438

Answers (2)

Johnathan Barclay
Johnathan Barclay

Reputation: 20353

I think the most readable way would be to group by Id, order each group and select the first Time from each:

var times = ExcelRecords.GroupBy(x => x.Id)
    .Select(grp => grp.OrderBy(item => Math.Abs(item.A + item.B + item.C)))
    .Select(grp => grp.First().Time);

Working Example

Upvotes: 0

Abion47
Abion47

Reputation: 24641

Using explicit for loops like this is throwing the power and flexibility of LINQ out the window. LINQ's power comes from enumerators which can be used on their own or in conjunction with a foreach loop.

You are also doing way more work than necessary using Distinct to first get a list and then using that list to selectively group the rows into batches. This is what GroupBy was designed for.

var times = ExcelRecords.GroupBy((row) => row.Id)
                        .Select((g) => g.Aggregate((min, row) => Math.Abs(row.a + row.b + row.c) < Math.Abs(min.a + min.b + min.c) ? row : min).Time)
                        .ToList();

Upvotes: 2

Related Questions