【问题标题】:ArrayList Sorting数组列表排序
【发布时间】:2011-07-16 02:00:38
【问题描述】:

我有一个包含大量字符串的 ArrayList。它需要根据 NameAgeAmt 三个字段(本质上是三个子字符串)进行就地排序。 Age 是第一个子字符串(位置 0-3),Name 是第二个 (3-6),Amt 是最后一个 (6-10) .这些参数的排序顺序非常重要,如下所示:

首先按姓名进行升序排序,然后按年龄进行升序排序(实际上在substring) 然后按 Amt 进行 降序 排序。就是这样。

我有这门课

public class ArrComparer : IComparer
{
    public int Compare(object x, object y)
    {
        string left = x.ToString();
        string right = y.ToString();
        string lhs = left.Substring(3, 6);
        string rhs = right.Substring(3, 6);
        return lhs.CompareTo(rhs);
    }
}

我使用它仅基于一个字段进行排序 - 通过调用命名

RecordList.Sort(new ArrComparer());

这让我可以根据 one 字段正确排序。问题是如何修改此代码以允许我根据所有三个AT ONCE、以正确的顺序并使用正确的 asc/desc 模式进行排序?

任何代码或提示将不胜感激。 (顺便说一句,如果您想知道使用通用 List<T> 不是这个项目的选项)。

【问题讨论】:

  • 为什么通用列表“不是一个选项”?为什么不使用具有三个字段的对象而不是字符串?
  • 你介意说为什么通用列表不是一个选项吗?
  • 想清楚。你如何进行常规的字母排序?你比较第一个字母。如果它们不同,那么您就知道顺序。如果它们相等,则比较第二个字母。等等。这里也一样。你如何按名字排序,然后是年龄,然后是数量?比较两个名字。他们不平等吗?然后你就知道顺序了。他们平等吗?然后比较年龄。他们不平等吗?然后你就知道顺序了。他们平等吗?然后比较金额。
  • “你介意说为什么通用列表不是一个选项吗?”这是一个公平的问题,简单的答案是,这是一个更大的项目的一小部分,该项目是不久前编写的,并且依赖于现有的数据结构。此时更改它们会比使用现有的更困难。
  • Eric,这本质上是从 COBOL 转换而来的,这就是结果在原始程序中的排序方式。此外,只要可以在这里使用 LINQ,它也是一种选择。在这一点上,我实际上对此知之甚少。顺便说一句,感谢所有回复的人。

标签: c# .net sorting arraylist icomparer


【解决方案1】:

ArrayList 仍然实现 IEnumerable,这意味着您可以在 linq 中使用简单的 orderby() 和 thenby() 扩展:

RecordList = new ArrayList(
         RecordList.Cast<string>().OrderBy(s => s.Substring(3,3))
                   .ThenBy(s => int.Parse(s.Substring(0,3)))
                   .ThenByDescending(s => double.Parse(s.Substring(6,4)))
          .ToArray());

其他表达方式包括构建更复杂的 .OrderBy() 或使用匿名类型将字符串组合为对象:

RecordList = new ArrayList(
       Record.Cast<string>().Select(s => new {source = s, age = int.Parse(s.Substring(0, 3)), name = s.Substring(3,3), amt = double.Parse(s.Substring(6,4))})
             .OrderBy(o => o.name)
             .ThenBy(o => o.age)
             .ThenByDescending(o => o.amt)
          .Select(o => o.source).ToArray());

我喜欢这个选项,因为它让你开始思考对象的术语。正确地玩你的牌,你可以跳过最后一个 .Select() 投影来保留对象而不是返回到字符串,这将节省以后必须重新进行所有解析的工作

如果这些不是一个选项(可能出于同样的原因,您不能使用 List),则可以轻松地修改现有的比较方法,如下所示:

public class ArrComparer : IComparer
{
    public int Compare(object x, object y)
    {
        int result;
        string left = x.ToString();
        string right = y.ToString();
        string lhs1 = left.Substring(3, 3);
        string rhs1 = right.Substring(3, 3);
        result = lhs1.CompareTo(rhs1);

        if (result == 0)
        {
           int lhs2 = int.Parse(left.Substring(0,3));
           int rhs2 = int.Parse(right.Substring(0,3));
           result = lhs2.CompareTo(rhs2);
        }

        if (result == 0)
        {
            double lhs3 = double.Parse(left.Substring(6,4));
            double rhs3 = double.Parse(right.Substring(6,4));
            result = rhs3.CompareTo(lhs3);
        }

        return result;
    }
}

【讨论】:

  • 这不会按照 OP 问题中的要求进行 就地 排序
  • @Joel:你应该使用Substring(3, 3) 而不是Substring(3, 6),因为第二个参数是长度。 Substring(6, 10) 也是如此。
  • @Vlad - 当然你是对的。在我的辩护中,我是从原始问题中的代码中提取队列的。
  • @Joel:除此之外,应该可以省略int.Parse(至少在Compare(...) 内部),因为年龄的数字被左填充到相同的小数位数。好吧,从另一方面来说,解析说明了进一步转换的可能性,所以它在教学上是合理的。
  • @Vlad - 即使我不需要它,我也会将 int.Parse() 保留在那里,除非它被证明会导致真正的性能下降或者我们开始看到格式错误字符串(异常),因为这有助于未来的程序员知道在字符串的那个位置期望什么。
【解决方案2】:

我建议将您的记录存储在一个对象中,并改为让它们具有可比性。

要使用您当前使用的相同方法比较所有三个字段,您只需提取所有三个数据并进行完整比较。

public class ArrComparer : IComparer
{
    public int Compare(object x, object y)
    {
        string left = x.ToString();
        string right = y.ToString();

        // Note I assumed indexes since yours were overlapping.
        string lage = left.Substring(0, 3);
        string lname = left.Substring(3, 3);
        string lamt = left.Substring(7, 3);

        string rage = left.Substring(0, 3);
        string rname = left.Substring(3, 3);
        string ramt = left.Substring(7, 3);

        // Compare name first, if one is greater return
        int result = lname.CompareTo(rname);
        if (result != 0)
            return result;

        // else compare age, if one is greater return
        result = lage.CompareTo(rage)
        if (result != 0)
            return result;

        // else compare amt if one is greater return
        result = lamt.CompareTo(ramt)
        if (result != 0)
            return result;

        // else they are equal
        return 0;
    }
}

【讨论】:

  • 您应该使用Substring(3, 3) 而不是Substring(3, 6),因为第二个参数是长度。 Substring(7, 10) 也是如此。同样,Substring(0, 2) 似乎是错误的。
  • @Vlad 哎呀!感谢您的提醒。 :)
  • 感谢所有的回复,伙计们!我需要一些时间来分析它们并试用它们,然后我会回来提供反馈。
【解决方案3】:

您可以逐个比较:

string left = (string)x;
string right = (string)y;

string lname = left.Substring(3, 3);
string rname = right.Substring(3, 3);
int result = lname.CompareTo(rname);
if (result != 0) return result;

string lage = left.Substring(0, 3);
string rage = right.Substring(0, 3);
int result = lage.CompareTo(rage);
if (result != 0) return result;

string lamt = left.Substring(6);
string ramt = right.Substring(6);
return -lamt.CompareTo(ramt);

【讨论】:

    【解决方案4】:

    如果您需要 IComparer,请尝试以下操作:

    public class ArrComparer : IComparer
    {
      public int Compare(object x, object y)
      {
        string left = x.ToString();
        string right = y.ToString();
        string leftName = left.Substring([whatever]);
        string rightName = right.Substring([whatever]);
    
        // First try comparing names
        int result = leftName.CompareTo(rightName);
        if (result != 0)
        {
          return result;
        }
    
        // If that didn't work, compare ages
        string leftAge = left.Substring([whatever]);
        string rightAge = right.Substring([whatever]);
        result = leftAge.CompareTo(rightAge);
        if (result != 0)
        {
          return result;
        }    
    
        // Finally compare amounts (descending)
        string leftAmt = left.Substring([whatever]);
        string rightAmt = right.Substring([whatever]);
        result = -leftAmt.CompareTo(rightAmt); // Minus for descending
    
        return result;
      }
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用 if(rhs == lhs) 之类的 if 语句来扩展 ArrCompare 与字符串的其他部分。 Accen deccend 是返回 -1 或 1 的交汇点

      【讨论】:

        猜你喜欢
        • 2014-05-03
        • 1970-01-01
        • 1970-01-01
        • 2011-05-21
        • 2013-10-31
        • 2011-01-07
        • 2015-04-13
        • 2014-06-04
        相关资源
        最近更新 更多