2

I have a List of strings that I want to bring in a certain order. Let's say the list contains a random amount of the strings "A", "B" and "C". And I have another List of strings containing the sort order.

For Example: For input:

  1. "A"

  2. "A"

  3. "C"

  4. "B"

  5. "B"

  6. "C"

with sort order List:

  1. "A"
  2. "B"
  3. "C"

I want to order this List that the output looks like this:

  1. "A"
  2. "B"
  3. "C"
  4. "A"
  5. "B"
  6. "C"

another example:

For Input:

  1. "A"
  2. "A"
  3. "C"
  4. "B"
  5. "C"

with sort order List:

  1. "A"
  2. "C"
  3. "B"

output should look like this:

  1. "A"
  2. "C"
  3. "B"
  4. "A"
  5. "C"

Note: I chose A, B and C only for the sake of simplicity so in my real application I won't be able to make use of any alphabetical order.

Is there any way to achieve the desired result? I wrapped my head around this for days and didn't come up with a solution. I tried to implement IComparer but I was struggling with the conditions for compare - method.

EffinEf
  • 23
  • 3
  • 1
    What did you try already? Give us some code to see that you do not want us to do your work for you. – Marv Mar 10 '17 at 13:16
  • Have you already tried something?`Where do you stuck? – Mighty Badaboom Mar 10 '17 at 13:16
  • Welcome to Stack Overflow. Check the Stack Overflow's [help on asking questions](http://stackoverflow.com/help/asking) first, please. Focus on [What topics can I ask about here](http://stackoverflow.com/help/on-topic), [What types of questions should I avoid asking?](http://stackoverflow.com/help/dont-ask), [How to ask a good question](http://stackoverflow.com/help/how-to-ask), [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) and [Stack Overflow question checklist](http://meta.stackoverflow.com/questions/260648/stack-overflow-question-checklist). – David Ferenczy Rogožan Mar 10 '17 at 13:17
  • I think you sortOrderList is confusing. It says: Sort by A then by B then by C but you really mean: give me chunks of ABC, if no ABC is avalable give me the rest sorted – Marc Wittmann Mar 10 '17 at 13:24
  • @Marc Wittmann Yes you are right. Maybe I was a bit unclear about this. – EffinEf Mar 10 '17 at 13:39
  • @Marv I will post my (not working) code as soon as possible. – EffinEf Mar 10 '17 at 13:43
  • I think corner cases should be specified as well. What results are you expecting if there are no matching string in the sort order? What if you have for example A, A, A, A, B, B, C, C, C and sort order A, B, C? – Eric Lemes Mar 10 '17 at 13:51
  • @Eric Lemes The output would be: A,B,C,A,B,C,A,C,A I will edit my original post with some real code to (hopefully) make things clear. – EffinEf Mar 10 '17 at 13:54

2 Answers2

2

I think this meets your needs:

        var list = new List<string> { "A", "B", "E", "A", "E", "A", "B", "E", "C", "B", "A", "D", "B", "E" };
        var sortOrder = new List<string> { "F", "E", "C", "A", "B", "D" };

        var resultSets = new List<List<string>> ();
        for (int i = 0; i < sortOrder.Count(); i++)
        {
            var currentLetter = sortOrder[i];
            for (int j = 0; j < list.Count(x=> x == currentLetter); j++)
            {
                if(resultSets.Count() < j + 1)
                {
                    resultSets.Add(new List<string>());
                }
                resultSets[j].Add(currentLetter);
            }
        }
        var result = string.Join(", ", resultSets.SelectMany(x => x));
        Console.WriteLine($"Results: { result}");
JR Kincaid
  • 823
  • 1
  • 7
  • 18
  • I assume the lookup is not needed. But you could use Skip if you were looking to sort by a discriminating field, but the rest of the object needed to be maintained in your results. Of course that wasn't a requirement so I left it out. – JR Kincaid Mar 10 '17 at 14:00
  • Minor note, `List` has a `Count` property that should be used for the outer for loop, instead of the `Count()` extension. The property is much faster. – Brandon Kramer Mar 10 '17 at 14:01
  • The same is true for the `resultSets.Count()` => `resultSets.Count`. – Brandon Kramer Mar 10 '17 at 14:02
  • Good point Brandon. :) – JR Kincaid Mar 10 '17 at 14:02
  • @JR Kincaid Thank you so much! You made my day ,Sir! – EffinEf Mar 10 '17 at 14:07
  • @EffinEf No worries. I think this would be a good interview question. Even if they didn't finish you would get to see them work through a problem. – JR Kincaid Mar 10 '17 at 14:15
  • If this were an interview question, I'd ask you what you think the performance of this algorithm is. It gets pretty hedious when the pattern and specially the input source start being large. @EffinEf – InBetween Mar 10 '17 at 14:54
  • Yes I have done enough SQL Server to know there are bajillion ways to optimize a query based on different shapes of a data set. You have to make assumptions to arrive at a solution. Lot of bench marking to be done to see which one would be best in most scenarios. – JR Kincaid Mar 10 '17 at 15:31
0

Although the accepted answer works, the performance is simply horrific if the input enumeration and/or pattern are large enough. You can get a significant increase in performance if you choose more adequate collections for the job::

public static IEnumerable<T> GetChunks<T>(this IEnumerable<T> source, IEnumerable<T> pattern)
    where T: IEquatable<T>
{
    var dictionary = pattern.ToDictionary(p => p, p => new Stack<T>());

    foreach (var item in source)
    {
        dictionary[item].Push(item);
    }

    var rest = source.Except(pattern);

    while (dictionary.Values.Any(q => q.Any()))
    {
        foreach (var p in pattern)
        {
            if (dictionary[p].Count  >0)
            {
                yield return dictionary[p].Pop();
            }
        }
    }

    foreach (var r in rest)
    {
        yield return r;
    }
}

The trick here is to build up front a cheap dictionary to leverage the faster look ups it provides.

I've also taken the liberty of deciding to add all unmatched elements to the end of the "ordered" result. This might not be what you want but the problem is underspecified in this area.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Thanks for your answer. After modifying your code slightly to fit my needs I will use it even though I already accepted JR Kincaids answer as your solution takes performance aspects into account. Some side note: I had to check for dictionary.ContainsKey(item) to make sure it works even if not all values of the pattern collection are present in the source collection(I wasn't spcific on that case so don't take it as criticism). – EffinEf Mar 10 '17 at 19:23
  • another side note:To make the "rest- stuff" work (which I don't need but thank you for your efforts!) like it is intended, the line `var rest =source.Except(pattern)` should be changed to something like this I think: `var rest = source.Where(x => source.Except(pattern).Any(y=>y.Equals(x)));`. – EffinEf Mar 10 '17 at 19:24