2017-12-19 10 views
1

Wie kann ich ein Array von Accounts sortieren, das nach einem Array von PersonRoles sortiert sein muss, aus dem hervorgeht, welche Rolle eine verknüpfte Person mit diesem Konto hat?Array nach einer Variablen sortieren, die mehrere Werte enthalten kann

Zum Beispiel

Bob ist der Eigentümer (O) Rechnungs 12, die Co-Unterzeichner (CO) Rechnungs 123, und der Empfänger der Rechnung 1234.

Joe ist der Eigentümer (O) Rechnungs 123 und Konto 1234 Die Begünstigte (BE) Rechnungs 12.

Wie würde ich das Array von Accountsfür Bob in der Reihenfolge des Eigentümer sortieren (O) zuerst, dann Co-Unterzeichner ('CO'), dann Begünstigt (BE) in dieser Reihenfolge.

Accounts Objektstruktur

Accounts 
{ 
    AccountNumber: 12, 
    PersonRoles: [ 
        { 
         AccountRoleCode: "O", 
         AccountRoleDescription: "Owner", 
         Person: "Bob" 
        }, 
        { 
         AccountRoleCode: "CO", 
         AccountRoleDescription: "Co-Signer", 
         Person: "" 
        }, 
        { 
         AccountRoleCode: "BE", 
         AccountRoleDescription: "Beneficiary", 
         Person: "Joe" 
        }, 
       ], 
    Balance: 5.00 
}, 
{ 
    AccountNumber: 123, 
    PersonRoles: [ 
        { 
         AccountRoleCode: "O", 
         AccountRoleDescription: "Owner", 
         Person: "Joe" 
        }, 
        { 
         AccountRoleCode: "CO", 
         AccountRoleDescription: "Co-Signer", 
         Person: "Bob" 
        }, 
        { 
         AccountRoleCode: "BE", 
         AccountRoleDescription: "Beneficiary", 
         Person: null 
        }, 
       ], 
    Balance: 100.00 
}, 
{ 
    AccountNumber: 1234, 
    PersonRoles: [ 
        { 
         AccountRoleCode: "O", 
         AccountRoleDescription: "Owner", 
         Person: "Joe" 
        }, 
        { 
         AccountRoleCode: "CO", 
         AccountRoleDescription: "Co-Signer", 
         Person: null 
        }, 
        { 
         AccountRoleCode: "BE", 
         AccountRoleDescription: "Beneficiary", 
         Person: "Bob" 
        }, 
       ], 
    Balance: 10000000.00 
} 

ursprüngliche Array von Accounts unter Bob aufgeführt zurückgegeben von API.

[1234, 12, 123]

gewünschte Anordnung sortiert.

[12, 123, 1234]

Mein erster Ansatz ist LINQ auf dem Array zu verwenden, aber ich bin nicht sicher, wie durch die Accounts[] und dann Schleife durch die PersonRoles[] Schleife der Accounts[] zu sortieren basierend auf den PersonRoles[].

Benötigt dies eine doppelte LINQ-Abfrage? Oder wäre ein anderer Ansatz besser?

+0

Verwenden Array.Sort (Array, IComparer): https://msdn.microsoft.com/en-us/library/aw9s5t8f (v = vs.110) .aspx – Gus

Antwort

1
public class AccountsByNameComparer : IComparer<Account> 
{ 
    private readonly string _name; 

    public AccountsByNameComparer(string name) 
    { 
     _name = name; 
    } 

    public int Compare(Account x, Account y) 
    { 
     return AccountSortValue(x).CompareTo(AccountSortValue(y)); 
    } 

    private int AccountSortValue(Account account) 
    { 
     if (account.PersonRoles.Any(role => role.AccountRoleCode == "O" 
              && role.Name == _name)) return 0; 
     if (account.PersonRoles.Any(role => role.AccountRoleCode == "CO" 
              && role.Name == _name)) return 1; 
     if (account.PersonRoles.Any(role => role.AccountRoleCode == "BE" 
              && role.Name == _name)) return 2; 
     return 3; 
    } 
} 

Jetzt können Sie tun

accounts.Sort(new AccountsByNameComparer("Bob")); 

oder

var sorted = accounts.OrderBy(a => a, new AccountsByNameComparer("Bob")); 

Die Vorteile sind

  • Sie Einheit testen Sie die comparer
  • Sie haben können verschiedene Vergleiche für die gleiche Klasse, falls Sie in einem anderen Kontext anders sortiert werden möchten
  • Wenn Sie es in eine separate Klasse einfügen, wird sichergestellt, dass Sie keinen Code duplizieren, wenn Sie an mehreren Stellen die gleiche Sortierung benötigen.

Dies ist ein bedauerlicherweise langer und verschachtelter Komponententest. Aber Sie müssen sicherstellen, dass es irgendwie funktioniert, und das ist normalerweise einfacher, als tatsächlich die ganze Anwendung auszuführen.

[TestClass] 
public class SortAccountsByNameTests 
{ 
    [TestMethod] 
    public void AccountsAreSortedInCorrectOrder() 
    { 
     var account1 = new Account 
     { 
      PersonRoles = new PersonRole[] 
      { 
       new PersonRole {AccountRoleCode = "BE", Name = "Bob"}, 
       new PersonRole {AccountRoleCode = "CO", Name = "Steve"}, 
       new PersonRole {AccountRoleCode = "O", Name = "John"}, 
      } 
     }; 
     var account2 = new Account 
     { 
      PersonRoles = new PersonRole[] 
      { 
       new PersonRole {AccountRoleCode = "CO", Name = "Bob"}, 
       new PersonRole {AccountRoleCode = "O", Name = "Steve"}, 
       new PersonRole {AccountRoleCode = "BE", Name = "John"}, 
      } 
     }; 
     var account3 = new Account 
     { 
      PersonRoles = new PersonRole[] 
      { 
       new PersonRole {AccountRoleCode = "O", Name = "Bob"}, 
       new PersonRole {AccountRoleCode = "CO", Name = "Steve"}, 
       new PersonRole {AccountRoleCode = "BE", Name = "John"}, 
      } 
     }; 
     var account4 = new Account 
     { 
      PersonRoles = new PersonRole[] 
      { 
       new PersonRole {AccountRoleCode = "O", Name = "Al"}, 
       new PersonRole {AccountRoleCode = "CO", Name = "Steve"}, 
       new PersonRole {AccountRoleCode = "BE", Name = "John"}, 
      } 
     }; 
     var unsorted = new Account[] {account1, account2, account3, account4}; 
     var comparer = new AccountsByNameComparer("Bob"); 
     var sorted = unsorted.OrderBy(a => a, comparer); 
     var expectedOrder = new Account[]{account3, account2, account1, account4}; 
     Assert.IsTrue(expectedOrder.SequenceEqual(sorted)); 
    } 
} 

Ich werde jetzt weggetragen werden. Was, wenn Sie die Reihenfolge ändern möchten, in der Sie diese sortieren, ohne den gesamten Vergleich neu zu schreiben? Oder magst du einfach nicht diese if Aussagen? (Sorry, das ist widerlich und nutzlos Warum mache ich das.?)

public class AccountsByNameComparer : IComparer<Account> 
{ 
    private readonly string _name; 
    private readonly List<string> _preferredRoleSequence; 

    public AccountsByNameComparer(string name, IEnumerable<string> preferredRoleSequence) 
    { 
     _name = name; 
     _preferredRoleSequence = preferredRoleSequence.ToList(); 
    } 

    public int Compare(Account x, Account y) 
    { 
     return AccountSortValue(x).CompareTo(AccountSortValue(y)); 
    } 

    private int AccountSortValue(Account account) 
    { 
     var rolesMatchedByName = account.PersonRoles 
      .Where(role => role.Name == _name); 
     var preferredRoleMatches = 
      rolesMatchedByName.Select(role => 
       _preferredRoleSequence.IndexOf(role.AccountRoleCode)) 
        .Where(index => index > -1) 
        .ToArray(); 
     if (preferredRoleMatches.Any()) 
      return preferredRoleMatches.Min(); 
     return Int32.MaxValue; 
    } 
} 

public class ExecutiveAccountsByNameComparer : AccountsByNameComparer 
{ 
    public ExecutiveAccountsByNameComparer(string name) 
     :base(name, new []{"O", "CO", "BE" }) { } 

} 
1

So etwas sollte funktionieren:

var result = accounts 
     .Select(a => new 
     { 
      a.AccountNumber, 
      RoleCodes = a.PersonRoles 
       .Where(r => r.Person == "Bob") 
       .Select(r => r.AccountRoleCode) 
       .ToArray(), 
     }) 
     .OrderBy(a => a.RoleCodes.Select(code => GetOrderByCode(code)).Max()) 
     .ThenBy(a => a.AccountNumber); 
1

Hier ist eine Möglichkeit, unter der Annahme, Bob hat eine und nur eine Rolle in allen Konten:

var ordered = 
    from a in Accounts 
    from r in a.PersonRoles 
    where r.Person == "Bob" 
    let ordering = r.AccountRoleCode == "O" ? 1 : r.AccountRoleCode == "CO" ? 2 : 3 
    orderby ordering 
    select a.AccountNumber; 

Variation, wenn Bob mehrere Rollen haben kann (und Bob hat eine Rolle in jedem Konto). In diesem Fall wählen wir zuerst die richtige Rolle, die ersten in der angegebenen Reihenfolge:

var ordered = 
    from a in Accounts 
    let bobsrole = (
     from r in a.PersonRoles 
     where r.Person == "Bob" 
     let o = r.AccountRoleCode == "O" ? 1 : r.AccountRoleCode == "CO" ? 2 : 3 
     orderby o 
     select (rolename: r,ordering: o) 
    ).First() 
    orderby bobsrole.ordering 
    select a.AccountNumber; 

Übungs für den Leser: was ist, wenn es Konten, wo Bob nicht beteiligt ist?

1

Sie können eine Linq-Anweisung wie folgt verwenden:

var givenPerson = "Bob"; 
Accounts.Where(a => a.PersonRoles.SelectMany(r => r.Person).Contains(givenPerson)) 
    .OrderBy(a => a, new CustomComparerForRoleCode(givenPerson)); 

eine benutzerdefinierte zu tun zu vergleichen, indem AccountRoleCode zu sortieren, werden Sie eine comparer Klasse benötigen:

public class CustomComparerForRoleCode : IComparer<PersonRole[]> 
{ 
    public string PersonInRole { get; set; } 

    public CustomComparerForRoleCode(string personInRole) { 
     this.PersonInRole = personInRole; 
    } 

    public int Compare(PersonRole[] x, PersonRole[] y) { 
     var roleCode = x.First(r => r.Person == PersonInRole).AccountRoleCode; 
     var otherRoleCode = y.First(r => r.Person == PersonInRole).AccountRoleCode; 
     if (roleCode == otherRoleCode) 
      return 0; 
     switch (roleCode) { 
      case "O": 
       return 1; 
      case "BE": 
       return -1; 
      case "CO": 
       if (otherRoleCode == "O") 
        return -1; 
       return 1; 
     } 
    } 
} 

Dies geht von einer Person kann nur eine Rolle pro Konto haben. Passen Sie wie erforderlich an.

Verwandte Themen