David Johnson
David Johnson

Reputation: 419

Getting Max Value Generic List For Each Item

I was wondering if you could help.

I have a list of information;

ClientNo SequenceNo

This could contain data such as;

Cli No: 0000001, seq: 1
Cli No: 0000001, seq: 2
Cli No: 0000001, seq: 3
Cli No: 0000001, seq: 3
Cli No: 0000002, seq: 1
Cli No: 0000002, seq: 1
Cli No: 0000002, seq: 2
Cli No: 0000002, seq: 2

I want to generate a list of the max sequence numbers.

Please note that the sequence number MAY be repeated numerous times, as per the example above.

So the list I would like to end up with from example about would be;

Cli No: 0000001, seq: 3
Cli No: 0000001, seq: 3
Cli No: 0000002, seq: 2
Cli No: 0000002, seq: 2

I have tried;

  var x = criticalNotesData.OrderBy(y => y.ClientNo)
         .ThenBy(z => z.SequenceNo).ToList();
  var m = x.Max(r => r.SequenceNo).ToList();

But the max is just providing the max sequence no in the list, rather than per client.

Thanks,

David

Upvotes: 1

Views: 2292

Answers (6)

Wasp
Wasp

Reputation: 3425

Assuming data like this:

var list = new []
{
    new { CliNo= "0000001", SeqNo= 1 },
    new { CliNo= "0000001", SeqNo= 2 },
    new { CliNo= "0000002", SeqNo= 1 },     
    new { CliNo= "0000001", SeqNo= 3 },
    new { CliNo= "0000001", SeqNo= 3 },
    new { CliNo= "0000002", SeqNo= 1 },
    new { CliNo= "0000002", SeqNo= 1 },
    new { CliNo= "0000002", SeqNo= 2 },
    new { CliNo= "0000002", SeqNo= 2 },
};

you can use this:

var output = 
    from i in list
    orderby i.CliNo, i.SeqNo
    group i by i.CliNo into g
    let max = g.Max(x => x.SeqNo)
    from r in g where r.SeqNo == max
    select r;

Is still valid what I said in a comment, the initial order of the data is important, in this code I took a general approach, but if you have guarantees about the initial order there are other strategies which can be more efficient if your source of data is expensive.

Upvotes: 0

cuongle
cuongle

Reputation: 75306

use GroupBy twice then OrderByDescending to get First instead of using Max, with this way, you don't need to create new object and still get the duplicate max items

var result = criticalNotesData.GroupBy(x => x.ClientNo)
                              .SelectMany(g => g.GroupBy(y => y.SequenceNo)
                                                .OrderByDescending(gg => gg.Key)
                                                .First()
                                           );

The result will be exactly what you want:

Cli No: 0000001, seq: 3 
Cli No: 0000001, seq: 3 
Cli No: 0000002, seq: 2 
Cli No: 0000002, seq: 2 

Upvotes: 3

Ray Hayes
Ray Hayes

Reputation: 15015

You want, for each of the 'Client' entries, to know the highest sequence. Thus that means the result should be a sequence (per client) rather than a single value as returned by Max. How about:

var maxSequence = criticalNotesData
      .GroupBy(n => n.ClientNo)
      .Select(g => new { Client = g.Key, Max = g.Max(i => i.SequenceNo) } );

foreach ( var entry in maxSequence )
{
    Console.WriteLine("Client {0} has max sequence of {1}", 
                      entry.Client, entry.Max); 
}

// Looking at your original, you now want only to know the (from the original)
// the entries matching MAX.  Since you now know the max per client, a second
// operation is needed.
var maxValueEntries = criticalNotesData
        .Where(n => maxSequence
                      .Single(c => c.Client == n.Client)
                      .Max == n.SequenceNo));

The maxValueEntries is doing a lot of lookups, so a Dictionary of values may be better.

// Turning the original into a Dictionary of clientNo returning the 
// max sequence.
var maxSequence2 = criticalNotesData
      .GroupBy(n => n.ClientNo)
      .Select(g => new { Client = g.Key, Max = g.Max(i => i.SequenceNo) } )
      .ToDictionary(c => c.Client, c => c.Max);
var maxValueEntries2 = criticalNotesData
        .Where(n => maxSequence2[n.Client] == n.SequenceNo));

Upvotes: 0

Vignesh.N
Vignesh.N

Reputation: 2666

you need GroupBy

var maxItems = criticalNotesData.GroupBy(p => p.ClientNo)
                                .Select(r => r.Max(q => q.SeqNo));

something like this.
or

var maxItemsClientWise = from p in criticalNotesData
                           group p by p.ClientNo into r
                           select new { MaxSeq = r.Max(g => g.SeqNo), 
                                        Client = r.First().ClientNo };

Upvotes: 1

Habib
Habib

Reputation: 223247

You can use the following query to get a list of your class object based on your criteria.

var query = (from t in list
            group t by t.CliNo into tgroup
            select new ClientSequence
            {
                CliNo = tgroup.Key,
                seq = tgroup.Max(r => r.seq)

            }).ToList();

It is assuming that your class structure is:

class ClientSequence
{
    public string CliNo { get; set; }
    public int seq { get; set; }
}

and your list is:

List<ClientSequence> list = new List<ClientSequence>
       {
           new ClientSequence{ CliNo= "0000001", seq= 1},
           new ClientSequence{ CliNo= "0000001", seq= 2},
           new ClientSequence{ CliNo= "0000001", seq= 3},
           new ClientSequence{ CliNo= "0000002", seq= 1},
           new ClientSequence{ CliNo= "0000002", seq= 1},
           new ClientSequence{ CliNo= "0000002", seq= 1},
           new ClientSequence{ CliNo= "0000002", seq= 2},
           new ClientSequence{ CliNo= "0000002", seq= 2},
   };

Output:

foreach (ClientSequence cs in query)
{
    Console.Write("Client No.: " + cs.CliNo);
    Console.WriteLine(" Max Sequence No.: " + cs.seq);
}

It will print

Client No.: 0000001 Max Sequence No.: 3
Client No.: 0000002 Max Sequence No.: 2

Upvotes: 1

Alessandro
Alessandro

Reputation: 3761

I can't try but I think this should work!

var m = x.GroupBy(r => r.ClientNo).Select(g => g.Max(x => x.SequenceNo));

EDIT: to know the client:

var m = x.GroupBy(r => r.ClientNo).Select(g => new { ClientID = g.Key, Max = g.Max(x => x.SequenceNo) });

Upvotes: 1

Related Questions