Thorin Oakenshield
Thorin Oakenshield

Reputation: 14682

C# : How to sort a list of object based on a list of string

I'm having two list like

 List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };

List<Test> l_lstStudents = new List<Test> 
                            { new Test { Age = 20, Name = "A0" }, 
                              new Test { Age = 21, Name = "A1" }, 
                              new Test { Age = 22, Name = "A2" }, 
                              new Test { Age = 23, Name = "A3" }, 
                              new Test { Age = 24, Name = "A4" }, 
                            };

Where Test is class like

 public class Test
    {
        public String Name;
        public Int32 Age;
    }

I need to sort the items in the l_lstStudents based on the l_lstNames. So the sorted list will be like,

List<Test> l_lstStudents = new List<Test> 
                        {  new Test { Age = 21, Name = "A1" }, 
                           new Test { Age = 23, Name = "A3" }, 
                           new Test { Age = 22, Name = "A2" }, 
                           new Test { Age = 24, Name = "A4" }, 
                           new Test { Age = 20, Name = "A0" }, 
                        };

Now i'm using for to do this.

Like

  1. Create a new list of Test objects.

  2. Iterate the loop for l_lstNames and fetch the Test object from l_lstStudent and add the same to the newly created list. Finally assign the new list to l_lstStudent

Please help me to do this in a simple way ( Linq or Lambda)

Upvotes: 9

Views: 3250

Answers (7)

Meysam
Meysam

Reputation: 18137

var newList = l_lstNames.Join(l_lstStudents, 
    s => s, 
    test => test.Name, 
    (s, test) => new Test { Name = s, Age = test.Age }
    ).ToList();

Upvotes: 1

Tamara Wijsman
Tamara Wijsman

Reputation: 12348

Using

l_lstStudents = l_lstStudents.OrderBy(x => l_lstNames.IndexOf(x.Name)).ToList();

in a small test program

public class Test
{
    public String Name;
    public Int32 Age;
}

class Program
{
    static void Main(string[] args)
    {
        List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };

        List<Test> l_lstStudents = new List<Test> 
                                    { new Test { Age = 20, Name = "A0" }, 
                                      new Test { Age = 21, Name = "A1" }, 
                                      new Test { Age = 22, Name = "A2" }, 
                                      new Test { Age = 23, Name = "A3" }, 
                                      new Test { Age = 24, Name = "A4" }, 
                                    };

        l_lstStudents = l_lstStudents.OrderBy(x => l_lstNames.IndexOf(x.Name)).ToList();
    }
}

results in

Age     21      int  
Name    "A1"    string 

Age     23      int  
Name    "A3"    string  

Age     22      int  
Name    "A2"    string 

Age     24      int  
Name    "A4"    string  

Age     20      int  
Name    "A0"    string 

and thus is:

List<Test> l_lstStudents = new List<Test> 
                        {  new Test { Age = 21, Name = "A1" }, 
                           new Test { Age = 23, Name = "A3" }, 
                           new Test { Age = 22, Name = "A2" }, 
                           new Test { Age = 24, Name = "A4" }, 
                           new Test { Age = 20, Name = "A0" }, 
                        };

Upvotes: 1

xanatos
xanatos

Reputation: 111860

Try something like:

List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };

List<Test> l_lstStudents = new List<Test> 
                            { new Test { Age = 20, Name = "A0" }, 
                              new Test { Age = 21, Name = "A1" }, 
                              new Test { Age = 22, Name = "A2" }, 
                              new Test { Age = 23, Name = "A3" }, 
                              new Test { Age = 24, Name = "A4" }, 
                            };

// We transform the list in a dictionary to make it faster to access.
// The first Select creates a new object with the index of the name and 
// the ToDictionary creates the Dictionary.
// Note that technically on a small list (like 5 elements) 
// it's probably faster to IndexOf directly the List... 
// This is the problem of premature optimization :-) :-)
// If you know the list will always be 5 elements then probably 
// IndexOf is more than enough.
var dict = l_lstNames.Select((p, i) => new { Index = i, Name = p })
                     .ToDictionary(p => p.Name, p => p.Index);

// We sort it. This works because 3 < 5 => 3 - 5 < 0, 5 > 3 => 5 - 3 > 0, 5 == 5 => 5 - 5 == 0
l_lstStudents.Sort((p, q) => dict[p.Name] - dict[q.Name]);

// We could do something like and it would be clearer.
l_lstStudents.Sort((p, q) => dict[p.Name].CompareTo(dict[q.Name]));

Upvotes: 1

user1082916
user1082916

Reputation:

Try out with following code:

 l_lstStudents = (from name in l_lstNames
                  join student in l_lstStudents
                  on name equals student.Name
                  select student).ToList<Test>();

Upvotes: 1

Tung
Tung

Reputation: 5444

Try this. Putting it in a dictionary may save some look up time:

int i = 0;
Dictionary<string, int> ordinalValues = l_lstNames.ToDictionary(name => name, name => i++);
var sortedStudents = l_lstStudents.OrderBy( a => ordinalValues[a.Name]).ToList();

Upvotes: 1

Botz3000
Botz3000

Reputation: 39610

Try this:

l_lstStudents = l_lstStudents.OrderBy(s => l_lstNames.IndexOf(s.Name)).ToList()

I think that expresses the intention quite clearly.

Upvotes: 9

Rawling
Rawling

Reputation: 50114

How about

var studentLookup = l_lstStudents.ToDictionary(s => s.Name, s => s);
return l_lstNames.Select(n => studentLookup[n]);

Upvotes: 2

Related Questions