在一个可以包含多个值的变量上对数组进行排序

时间:2017-12-19 21:46:28

标签: c# arrays linq sorting

如何对需要在Accounts数组上排序的PersonRoles数组进行排序,以确定关联人员与该帐户的关联角色?

例如,

Bob是帐户O的所有者(12),帐户CO的共同签名者(123)和帐户受益人{{1} }。

Joe是帐户1234的所有者(O)和帐户123帐户1234的帐户BE

我如何首先按照所有者(12)的顺序为Bob {/ strong>排序Accounts 数组,然后是Co-signer(' CO&#39 ;),然后按顺序受益人(O)。

帐户对象结构

BE

从Bob返回的Bob下列出的原始帐户数组。

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 }

所需的排序数组。

[1234, 12, 123]

我最初的方法是在阵列上使用LINQ,但我不确定如何遍历[12, 123, 1234]然后循环遍历Accounts[]以基于PersonRoles[]排序在Accounts[]

这是否需要双LINQ查询?或者另一种方法会更好吗?

4 个答案:

答案 0 :(得分:1)

这样的事情应该有效:

    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 :(得分:1)

这是一种方式,假设Bob在所有帐户中只有一个角色:

<input type="text" id="t1">
<input type="text" id="t2" pattern="^\d{5}$">

变化,如果Bob可以有多个角色(并且Bob在每个帐户中都有一个角色)。在这种情况下,我们首先选择正确的角色,即指定顺序中的第一个角色:

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;

读者的练习:如果有不涉及Bob的帐户怎么办?

答案 2 :(得分:1)

您可以使用这样的Linq语句:

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

要进行自定义比较以按AccountRoleCode排序,您需要一个比较器类:

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;
        }
    }
}

这假定一个人每个帐户只能有一个角色。根据需要进行调整。

答案 3 :(得分: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;
    }
}

现在你可以做到

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

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

这样做的好处是

  • 您可以对比较器进行单元测试
  • 对于同一个类,您可以使用不同的比较器,以防在某些其他上下文中进行不同的排序
  • 如果您发现自己需要在多个地方进行相同的排序,请将其放在一个单独的类中,以确保您不会重复代码。

这是一个令人遗憾的漫长而复杂的单元测试。但是你必须验证它是否以某种方式工作,这通常比实际运行整个应用程序更容易。

[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));
    }
}

我现在要被带走了。如果要在不重写整个比较器的情况下更改排序顺序,该怎么办?或者你不喜欢那些if陈述? (对不起,这是令人讨厌和无用的。为什么我这样做?)

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" }) { }

}