Hai bisogno di un metodo di ordinamento per la raccolta coordinata e dettagliata [chiusa]

4

Ho un componente che emette un ICollection del seguente tipo:

public class CoordinatedInjectableValue
{
    public int XOffset { get; set; }

    public int YOffset { get; set; }

    public object Value { get; set; }
}

Questo è usato per descrivere ciò che è fondamentalmente un set di dati, quindi per un dato valore XOffset, la proprietà "Value" di tutti questi oggetti sarà dello stesso tipo (int, string, ecc.). La proprietà Value è sempre un tipo predefinito, non un tipo personalizzato.

La mia sfida è che mi piacerebbe utilizzare questa collezione per costruire una sorta di struttura di set di dati che consente l'ordinamento sulle colonne.

Ad esempio, se avessi una collezione che assomigliava a questa:

new List<CoordinatedInjectableValue>
{
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 0, Value = 20},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 1, Value = 10},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 2, Value = 30},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 0, Value = "CCC"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 1, Value = "BBB"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 2, Value = "AAA"}
};

Voglio un metodo per poter riordinare in base alla prima "colonna" (cioè, dove XOffset = 0), in modo che la raccolta assomigli a questo:

new List<CoordinatedInjectableValue>
{
    // Ordered by value where XOffset = 0
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 0, Value = 10}, 
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 1, Value = 20},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 2, Value = 30},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 0, Value = "BBB"}, // Note this has also been re-ordered
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 1, Value = "CCC"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 2, Value = "AAA"}
}

E se l'ho ordinato dalla seconda colonna (XOffset = 1), succederà quanto segue:

new List<CoordinatedInjectableValue>
{
    // Ordered by value where XOffset = 1
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 0, Value = 30}, // Note this has also been re-ordered
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 1, Value = 10},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 2, Value = 20},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 0, Value = "AAA"}, 
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 1, Value = "BBB"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 2, Value = "CCC"}
};

Come puoi immaginare, ho qualche difficoltà a farlo, anche per l'ordinamento a livello singolo. Non ho nemmeno provato a implementare l'ordinamento a più livelli (ad esempio, ordinare per questo, quindi quello).

Sto cercando un qualche tipo di approccio logico a questo. Preferirei non dover cambiare il componente che restituisce questi dati, dato che è fornito da una terza parte, e sarebbe costoso cambiarli.

    
posta tom redfern 23.07.2015 - 16:30
fonte

2 risposte

3

Questo non è solo un problema di classificazione. Stai anche aggiornando i valori YOffset in base all'ordinamento. Supponiamo quindi che i dati di input siano validi (cioè non mancano offset, tipo consistente di proprietà Value). Ecco il codice:

Dati sorgente:

var givenList = new List<CoordinatedInjectableValue>
{
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 0, Value = 20},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 1, Value = 10},
    new CoordinatedInjectableValue {XOffset = 0, YOffset = 2, Value = 30},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 0, Value = "CCC"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 1, Value = "BBB"},
    new CoordinatedInjectableValue {XOffset = 1, YOffset = 2, Value = "AAA"}
};

LINQ:

var sortedByFirstColumn = givenList
.GroupBy(v => v.YOffset)
.OrderBy(grp => grp.First(x => x.XOffset == 0).Value)
.SelectMany((grp, i) =>
    grp.Select(v => new CoordinatedInjectableValue
    {
        XOffset = v.XOffset,
        YOffset = i,
        Value = v.Value
    }))

Verifica il risultato:

sortedByFirstColumn
.OrderBy(v => v.XOffset)
.ThenBy(v => v.YOffset)
.ToList()
.ForEach(v => Console.WriteLine("{0}\t{1}\t{2}", v.XOffset, v.YOffset, v.Value));

L'output:

0   0   10
0   1   20
0   2   30
1   0   BBB
1   1   CCC
1   2   AAA

Nota: ho mantenuto ordinatoByFirstColumn come IEnumerable e non ordinato, ma lo aggiusto come richiesto dall'applicazione.

    
risposta data 23.07.2015 - 19:03
fonte
1
    public class CIVComparer : IComparer<CoordinatedInjectableValue>
{
    private string SortBy { get; set; }
    private bool SortAscending { get; set; }

    public CIVComparer()
    {
        SortBy = "default";
        SortAscending = true;
    }

    public CIVComparer(string sortBy, bool sortAscending)
    {
        SortBy = sortBy;
        SortAscending = sortAscending;
    }

    #region IComparer<CoordinatedInjectableValue> Members

    int IComparer<CoordinatedInjectableValue>.Compare(CoordinatedInjectableValue x, CoordinatedInjectableValue y)
    {
        CoordinatedInjectableValue cx, cy;
        if (SortAscending)
        {
            cx = x;
            cy = y;
        }
        else
        {
            cx = y;
            cy = x;
        }
        int result = 0;
        var useCulture = new System.Globalization.CultureInfo("en-US");
        string xCaseNum, yCaseNum;

        switch (SortBy)
        {
            case "XOffset":
                result = new Comparer(useCulture).Compare(cx.XOffset, cy.XOffset);
                if (result == 0) { result = new Comparer(useCulture).Compare(cx.Value, cy.Value); }
                if (result == 0) { result = new Comparer(useCulture).Compare(xCaseNum, yCaseNum); }
                break;

            case "Value":
                result = new Comparer(useCulture).Compare(cx.Value, cy.Value);
                if (result == 0) { result = new Comparer(useCulture).Compare(cx.XOffset, cy.XOffset); }
                if (result == 0) { result = new Comparer(useCulture).Compare(cx.YOffset, cy.YOffset); }
                break;

            case "default":
            default:
                result = new Comparer(useCulture).Compare(cx.YOffset, cy.YOffset);
                if (result == 0) { result = new Comparer(useCulture).Compare(cx.XOffset, cy.XOffset); }
                break;
        }
        return result;
    }

    #endregion
}

Quindi, quando si desidera ordinare un elenco:

    List<CoordinatedInjectableValue> CIVs = MethodWhichReturnsList(argsForMethod);
    CIVComparer civComparer = new CIVComparer(sortBy, sortAscending);
    CIVs.Sort(civComparer);

La logica nel Comparatore sarà ovviamente diversa, ma, come puoi vedere, offre molta flessibilità.

    
risposta data 23.07.2015 - 17:54
fonte

Leggi altre domande sui tag