4

Object structure A class has multiple lists of data. Class List1 of double List2 of double List3 of double List4 of double

Objective: Sort multiple lists based on one list. E.g. List1 in ascending order and all other lists to follow that order to maintain individual point relativity based on index.

Initial implementations that I have tried are:

  1. Zip List2, 3 and 4 with List 1 and then sort based on List 1. Then combine sorted lists again.

e.g.

    var x1 = testData.SelectMany(d => d.xData).ToList();
    var y1 = modelData.SelectMany(d => d.yData).ToList();
    var y2 = modelData.SelectMany(d => d.y2Data).ToList();
    var sampleValues = x1.Zip(y1, (x, y) => new { X = x, Y = y }).OrderBy(v => v.X);
    var sampleValues1 = x1.Zip(y2, (x, y) => new { X = x, Y2 = y }).OrderBy(v => v.X);`

//Next select X, Y from sampleValues and select Y2 from sampleValue2
  1. Tried using SelectMany on different lists and then put that into an anonymous type. SelectMany does not work with this as it needs definite data type to return.

Anything that I am missing in these approaches or there is another approach required to get what I am trying to achieve.

Also having a class with all this data or lists as individual rows and data inside columns is not an option for me. This is because I have a list of objects having these properties. So eventually I want to merge list data across objects sampleData lists and then sort and use that data.

Feel free to let me know in case further information is required.

4
  • 4
    Is there a specific question somewhere in here? Or do you just want us to do your homework for you? Commented Dec 4, 2015 at 14:13
  • 1
    Any reason you're not wrapping the four values in a class, struct or tuple? Commented Dec 4, 2015 at 14:20
  • @roryap I have tried approaches and for a workaround I am working to convert this class from different lists to a table structure where I can use it to sort on one column. Its just that I was curious if there is a way to efficiently sort multiple lists based on one list and preserve the index matching across these lists.? Let me know in case this sounds like a question and I will be happy to update the main question? Commented Dec 4, 2015 at 14:28
  • @Kvam The code evolved such that individual lists were being used for different purpose in terms of presentation on the UI. Commented Dec 4, 2015 at 14:30

3 Answers 3

6

There's a not well-known method Array.Sort that sorts an array according to the order of a second array. I made a small extension method that utilizes this oldie:

public static class ICollectionExtensions
{
    public static IEnumerable<TSource> SortLike<TSource,TKey>(this ICollection<TSource> source, 
                                        IEnumerable<TKey> sortOrder)
    {
        var cloned = sortOrder.ToArray();
        var sourceArr = source.ToArray();
        Array.Sort(cloned, sourceArr);
        return sourceArr;
    }
}

You can use this by calling ...

var list21 = list2.SortLike(list1);

The advantage of this method is that it's extremely fast, despite the two ToArray() calls in it. ToArray() creates a shallow copy of the collection, which only takes a couple of milliseconds with a list of 10 million items. Array.Sort is fast because it selects the best sorting algorithm for the size of the array.

Sign up to request clarification or add additional context in comments.

2 Comments

Wonderful. Thanks for the concise solution. This has been a great learning !
Imho, best solution for the problem at hand because it is concise, formulated as extension method, adaptable to collection size, and definitely the fastest among all other answers here for large collections. Did I forget to mention it hardly creates any instances that need to be GCed...
2

You could do this:

var listA = new List<double> { 1.0, 2.0, 3.0 };
var listB = new List<double> { 1.1, 2.1, 3.1 };
var listC = new List<double> { 1.2, 2.2, 3.2 };
var listD = new List<double> { 1.3, 2.3, 3.3 };

var items = new List<Tuple<double, double, double, double>>();
for (var i = 0; i < listA.Count; ++i)
    items.Add(Tuple.Create(listA[i], listB[i], listC[i], listD[i]));

var sorted = items.OrderBy(x => x.Item1);

listA = sorted.Select(x => x.Item1).ToList();
listB = sorted.Select(x => x.Item2).ToList();
listC = sorted.Select(x => x.Item3).ToList();
listD = sorted.Select(x => x.Item4).ToList();

You're probably better off doing something like:

public class MyClass
{
    public double A { get; set; }
    public double B { get; set; }
    public double C { get; set; }
    public double D { get; set; }
}

And then work on List<MyClass> instead of four different lists.

Comments

1

Here you go

double[] input1 = ..., input2 = ..., input3 = ..., input4 = ...;
var sortIndex = Enumerable.Range(0, input1.Count).OrderBy(i => input1[i]).ToList();
var output1 = sortIndex.Select(i => input1[i]).ToList();
var output2 = sortIndex.Select(i => input2[i]).ToList();
var output3 = sortIndex.Select(i => input3[i]).ToList();
var output4 = sortIndex.Select(i => input4[i]).ToList();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.