【问题标题】:Sort array on one variable that can hold multiple values对一个可以保存多个值的变量的数组进行排序
【发布时间】:2017-12-19 21:46:28
【问题描述】:

如何对需要在PersonRoles 数组中排序的Accounts 数组进行排序,该数组说明关联人员在该帐户中的角色?

例如,

Bob 是帐户 12 的所有者 (O)、帐户 123 的共同签名人 (CO) 以及帐户 1234 的受益人。

Joe 是帐户 123 的所有者 (O),帐户 1234 是帐户 12 的受益人 (BE)。

我如何将Accounts for Bob 的数组按所有者 (O) 的顺序排序,然后是共同签名者 ('CO'),然后是受益人 (BE ) 按此顺序。

账户对象结构

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
}

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

[1234, 12, 123]

所需的排序数组。

[12, 123, 1234]

我最初的方法是在数组上使用 LINQ,但我不确定如何循环遍历 Accounts[],然后循环遍历 PersonRoles[] 以根据 PersonRoles[]Accounts[] 进行排序。

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

【问题讨论】:

标签: c# arrays linq sorting


【解决方案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" }) { }

}

【讨论】:

    【解决方案2】:

    这样的事情应该可以工作:

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

    【讨论】:

      【解决方案3】:

      这是一种方法,假设 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 可以拥有多个角色(并且 Bob 在每个帐户中都有一个角色)。在这种情况下,我们首先选择正确的角色,按指定的顺序排列第一个:

      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;
      

      读者练习:如果有 Bob 没有参与的帐户怎么办?

      【讨论】:

        【解决方案4】:

        你可以像这样使用 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;
                }
            }
        }
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-12-05
          • 1970-01-01
          • 1970-01-01
          • 2014-04-01
          • 2012-12-18
          • 1970-01-01
          • 1970-01-01
          • 2012-12-07
          相关资源
          最近更新 更多