Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

168
Visualizações
C# rearrange List by pattern

I have a List<TimeAndCode> of objects. Every object contains a Code and a TimeSpan value.

public struct TimeAndCode {
    public TimeSpan Time { get; set; }
    public string Code { get; set; }
}

The occuring codes are pre defined as "CO", "GO" and "BT".

When the Time property hits the same value, the order of the list needs to be in a certan format. e.g. (JSON representation for readability)

[
    {"Time" : "08:00:00", "Code" : "CO" },
    {"Time" : "09:00:00", "Code" : "GO" },
    {"Time" : "09:30:00", "Code" : "CO" },
    {"Time" : "09:30:00", "Code" : "GO" },
    {"Time" : "09:30:00", "Code" : "CO" },
    {"Time" : "09:30:00", "Code" : "GO" },
    {"Time" : "09:30:00", "Code" : "BT" },
    {"Time" : "12:30:00", "Code" : "CO" },
    {"Time" : "12:30:00", "Code" : "GO" },
    {"Time" : "13:30:00", "Code" : "CO" },
    {"Time" : "13:30:00", "Code" : "GO" },
    {"Time" : "13:30:00", "Code" : "BT" },
    {"Time" : "13:30:00", "Code" : "CO" },
    {"Time" : "13:30:00", "Code" : "GO" }
]

So the pattern is either CO -> GO or GO -> BT or CO -> GO -> BT -> CO -> GO.

There might be a good old if else solution, but I'm looking for a nice and easy to use LINQ solution. (e.g. list.OrderBy( x => x.Time ).RearrangeBy( x => x.Code, pattern ); )

EDIT

The custom ordering must only take place, when the codes are in the same time slot.

The easiest pattern is:

CO -> GO -> CO -> GO -> CO -> GO 

But there is also a possibility that a BT can be present:

CO -> GO -> BT -> CO -> GO -> BT -> CO -> GO -> BT -> CO

But there is also a possibility that there is only one BT:

CO -> GO -> BT -> GO -> GO -> CO -> GO -> CO -> GO
CO -> GO -> CO -> GO -> BT -> CO -> GO -> CO -> GO
CO -> GO -> CO -> GO -> CO -> GO -> BT -> CO -> GO
CO -> GO -> BT -> CO -> GO -> CO -> GO -> BT -> CO -> GO

So tha major pattern is CO -> GO -> BT.

EDIT 2 Solved

I created an extension method. May be this code is useful for somebody. (room for improvement)

public static IEnumerable<TSource> RearrangeByPattern<TSource, TKey>(this IEnumerable<TSource> list, Func<TSource, TKey> keySelector, IEnumerable<TKey> pattern)
{
    var groups = list.GroupBy(keySelector).OrderBy(x => pattern.IndexOf(x.Key));
    var maxOccurences = groups.Select(x => x.Count()).Max();

    var result = new List<TSource>();
    for (var i = 0; i < maxOccurences; i++)
    {
        foreach (var group in groups)
        {
            if (group.Count() > i)
            {
                result.Add(group.ElementAt(i));
            }
        }
    }

    return result;
}

Now I can use it like this:

var list = new List<TimeAndCode>();
... // add values
var ordered = list.OrderBy(x => x.Date).RearrangeByPattern( x => x.Code, new string[] { "CO", "GO", "BT" });
over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

Here is a suggestion where TimeAndCode.Code is an enum rather than a string.

It will always order entries with indentical timespans in a repeated CO, GO, BT pattern; meaning that if e.g. five entries with identical timespans has the following Code selection: 2 x CO, 1 x BT, 2 x GO, it will always order them as CO, GO, BT, CO, GO (as opposed to CO, GO, CO, GO, BT).

I achieve this by generating an OrderBy property based on the timespan, an index (generated inside a nested group) and the numerical Code value for each entry.

Using the following types:

public struct TimeAndCode
{
    public TimeSpan Time { get; set; }
    public Code Code { get; set; }
}

public enum Code
{
    Undefined,
    CO,
    GO,
    BT
}

we can write the following expression:

List<TimeAndCode> result = list
    .GroupBy(entry => entry.Code)
    .SelectMany(gr => gr
        .GroupBy(entry => entry.Time)
        .SelectMany(gr => gr.Select((entry, index) => (
            OrderBy: entry.Time.ToString() + index + (int)entry.Code,
            TimeAndCode: entry))))
    .OrderBy(entry => entry.OrderBy)
    .Select(entry => entry.TimeAndCode)
    .ToList();

where list is a List<TimeAndCode>.


Using example input as follows:

List<TimeAndCode> list = new List<TimeAndCode> 
{
    new TimeAndCode { Time = new TimeSpan(09, 00, 00), Code = Code.GO },
    new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.BT },
    new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.GO },
    new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.GO },
    new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.GO },
    new TimeAndCode { Time = new TimeSpan(08, 00, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.BT },
    new TimeAndCode { Time = new TimeSpan(12, 30, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(12, 30, 00), Code = Code.GO },
    new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.CO },
    new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.GO },
};

, after applying the Linq expression, we can then print the result

foreach (var entry in result)
{
    Console.WriteLine("Time: " + entry.Time + " Code: " + entry.Code);
}

and get the following output:

Time: 08:00:00 Code: CO
Time: 09:00:00 Code: GO
Time: 09:30:00 Code: CO
Time: 09:30:00 Code: GO
Time: 09:30:00 Code: BT
Time: 09:30:00 Code: CO
Time: 09:30:00 Code: GO
Time: 12:30:00 Code: CO
Time: 12:30:00 Code: GO
Time: 13:30:00 Code: CO
Time: 13:30:00 Code: GO
Time: 13:30:00 Code: BT
Time: 13:30:00 Code: CO
Time: 13:30:00 Code: GO
over 4 years ago · Santiago Trujillo Relatório

0

It's a variation of Astrid's:

var r =  list.GroupBy(tc => tc)
        .SelectMany(g => g.Select((tc, i) => (tc, i)))
        .OrderBy(t => (t.tc.Time, t.i, t.tc.Code))
        .Select(t => t.tc);

With the following precursor setup:

public record TimeAndCode(TimeSpan Time, Code Code);

public enum Code { CO, GO, BT }

    ...

    var list = new List<TimeAndCode> 
        {
            new (TimeSpan.FromHours(8), Code.CO),
            new (TimeSpan.FromHours(9), Code.GO),
            new (TimeSpan.FromHours(9.5), Code.CO),
            new (TimeSpan.FromHours(9.5), Code.GO),
            new (TimeSpan.FromHours(9.5), Code.GO),
            new (TimeSpan.FromHours(9.5), Code.CO),
            new (TimeSpan.FromHours(9.5), Code.BT),
            new (TimeSpan.FromHours(12.5), Code.CO),
            new (TimeSpan.FromHours(12.5), Code.GO),
            new (TimeSpan.FromHours(13.5), Code.CO),
            new (TimeSpan.FromHours(13.5), Code.BT),
            new (TimeSpan.FromHours(13.5), Code.GO),
            new (TimeSpan.FromHours(13.5), Code.CO),
            new (TimeSpan.FromHours(13.5), Code.GO),
        };


TimeAndCode is a record, which means it gains some useful properties for sorting and comparing. It can be simply grouped because it is automatically equal to another TimeAndCode with the same data

Grouping by the time and code results in a list-of-lists; the two 9:30 COs go in a list. Passing this to Select((tc,i) means that i is 0 for the first and 1 for the second etc.. All we do with this is promote it to a tuple of the tc and the i because we'll need them later, and we SelectMany to undo the GroupBy that allowed us to count the same Time/Code

We OrderBy another tuple; tuples sort by their values in order, so to order by 3 things a, b, and c, we can OrderBy a tuple of (a, b, c)

And all that is left to do at the end is select the tc back to make a list of TimeAndCode, sorted something like what you want

Like Astrid's it also doesn't put the BT at the end for 9:30 - it goes in the middle, like 13:30's does.. But you haven't provided an explanation for why 9:30's BT is at the end...

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda