2

In C#,I have List of Employee object. Employee class is

public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }
}

In List objected are sorted based on Employee.ID. I have an array of int which is basically Employee.ID which I want on top of the list and in list,order must remain same as in array.

If I hava input like this List:

[
  {ID:1,Name:A},
  {ID:2,Name:B},
  {ID:3,Name:AA},
  {ID:4,Name:C},
  {ID:5,Name:CD},
  .
  .
  {ID:100,Name:Z}
] 

and Array: {2,3,1}

Then I want Output List:

[
  {ID:2,Name:B},
  {ID:3,Name:AA},
  {ID:1,Name:A},
  {ID:4,Name:C},
  {ID:5,Name:CD},
  .
  .
  {ID:100,Name:Z}
]

And I have done this

foreach (int i in a)
{
                    list = list.OrderBy(x => x.ID != i).ToList();
}
//a is array
//list is List 

Any better Solution.Thanks in advance.

yajiv
  • 2,901
  • 2
  • 15
  • 25
  • Possible duplicate of [C# List<> Sort by x then y](https://stackoverflow.com/questions/289010/c-sharp-list-sort-by-x-then-y) – Simon Price Sep 06 '17 at 07:18
  • Although I have marked as a duplicate, this is the sort of thing that youre looking for based on your question. https://stackoverflow.com/questions/289010/c-sharp-list-sort-by-x-then-y – Simon Price Sep 06 '17 at 07:19
  • It´s not a duplicate of this question. There´s not an *attribute* that can be used to order the array, but *another array*. – MakePeaceGreatAgain Sep 06 '17 at 07:24
  • Is it guarenteed that there will be no gaps in the list of Employees? i.e. ID=1, ID=2, ID=4, ID=5... – Ashley Pillay Sep 06 '17 at 07:30
  • @HimBromBeere The attribute is `ID`. And the comparison attribute is the index of that `ID` in the second (order defining) array. – Ivan Stoev Sep 06 '17 at 07:45

5 Answers5

2

After you got your list sorted based on the ID just iterate the array and move the elements. In order to do this you need to first remove and then insert the item at the correct position.

for(int i = 0; i < myArray.Length; i++)
{
    var e = myList.Single(x => x.Id == myArray[i]);
    myList.Remove(e);
    myList.Insert(i, e);
}

You may also want to use SingleOrDefault instead of Single to verify that myList even contains the element with the current id, e.g. when your array contains [2, 3, 101]

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • I agree with @HimBromBeere. Additionally, you should consider the fact that this list is not containing the item. – Tunahan Sep 06 '17 at 07:33
2

To add another version to the mix. The complete sorting can be done in one go:

list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? int.MaxValue : i; }).ToList();

where list is the EmployeeList and a the indices array. (NB, the for loop is not needed, the above should do both sortings).

Inside the OrderBy callback, if the id is not inside a, int.MaxValue is returned to place it after the ones inside the array (a.Length would work as well). OrderBy should maintain the original order of the enumeration (list) for those elements that return the same value.

PS, if you want to sort first by index inside a and the rest on the ids (not necessarily the original order), you can use the following (as long as a.Length + largest ID < int.MaxValue) : list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? a.Length + e.ID : i; }).ToList();

Me.Name
  • 12,259
  • 3
  • 31
  • 48
1

Here's a way to do it in pure LINQ, without changing the original sequence.

Broken into steps to see what's going on.

    public static void Main()
    {
        var employeeList = new List<Employee>()
        {
            new Employee(){ ID= 1,Name= "A"},
            new Employee() { ID= 2,Name= "B"},
            new Employee() { ID= 3,Name= "AA"},
            new Employee() { ID= 4,Name= "C"},
            new Employee() { ID= 5,Name= "CD"},
            new Employee() { ID= 100,Name= "Z"}
        };

        var orderByArray = new int[] { 2, 3, 1, 100, 5, 4 };

        var sortPos = orderByArray.Select((i, index) => new { ID = i, SortPos = index });
        var joinedList = employeeList.Join(sortPos, e => e.ID, sp => sp.ID, (e, sp) => new { ID = e.ID, Name = e.Name, SortPos = sp.SortPos });
        var sortedEmployees = joinedList.OrderBy(e => e.SortPos).Select(e => new Employee { ID = e.ID, Name = e.Name });

    }
Ashley Pillay
  • 868
  • 4
  • 9
1

Try this using LINQ:

List<Employee> employees = ...
int[] ids = ...
var orderEmployees = ids.Select(id => employees.Single(employee => employee.ID == id))
      .Concat(employees.Where(employee => !ids.Contains(employee.ID)).ToList();

Foreach id in ids array we will grab the matching employee and we will concat to it all the employees that their id does not exist in ids array.

YuvShap
  • 3,825
  • 2
  • 10
  • 24
1

I like to use a special Comparer for that, it seems clearer to me, though a bit more code. It hides the complexity of the sort in the comparer class, and then you can just call it with :

theList.OrderBy(x => x.id, new ListOrderBasedComparer(sortList));

It will sort according to any list passed to the comparer when instantiating, and will put elements not in the "known sort list" at the end.

You can of course adapt it to your special needs.

public class ListOrderBasedComparer: Comparer<int>
{
    private List<int> sortList;
    public ListOrderBasedComparer(List<int> sortList)
    {
        // if you want you can make constructor accept arrays and convert it 
        // (if you find that more convenient)
        this.sortList = sortList;
    }

    public override int Compare(int x, int y)
    {
        var indexOfX = sortList.FindIndex(a => a == x);
        var indexOfY = sortList.FindIndex(a => a == y);

        // handle elements not in sortArray : if not in sort array always assume they should be "less than the others" and "equal between them".
        if (indexOfX == -1 && indexOfY == -1) return 0;
        if (indexOfY == -1) return -1;
        if (indexOfX == -1) return 1;

        // if elements are in sortArray (FindIndex returned other than -1), use usual comparison of index values
        return indexOfX.CompareTo(indexOfY); 
    }
}

Example on how to use it, with Linq :

public class TestCompare
{
    public void test ()
    {
        var myArray = new MyClass[]
        {
            new MyClass { id = 1, name = "A" },
            new MyClass { id = 2, name = "B" },
            new MyClass { id = 3, name = "C" },
            new MyClass { id = 4, name = "D" },
            new MyClass { id = 5, name = "E" },
            new MyClass { id = 6, name = "F" },
        };

        var myArray2 = new MyClass[]
        {
            new MyClass { id = 1, name = "A" },
            new MyClass { id = 2, name = "B" },
            new MyClass { id = 0, name = "X" },
            new MyClass { id = 3, name = "C" },
            new MyClass { id = 4, name = "D" },
            new MyClass { id = 23, name = "Z"},
            new MyClass { id = 5, name = "E" },
            new MyClass { id = 6, name = "F" },
        };

        var sortList = new List<int> { 2, 3, 1, 4, 5, 6 };

        // good order
        var mySortedArray = myArray.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList(); 
        // good order with elem id 0 and 23 at the end
        var mySortedArray2 = myArray2.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();

    }
}

public class MyClass
{
    public int id;
    public string name;
}
Pac0
  • 21,465
  • 8
  • 65
  • 74