简体   繁体   English

按数字的总和排序

[英]Order by sum of digits of number

Example:例子:

a = "56 65 74 100 99 68 86 180 90", ordered by numbers weights becomes: "100 180 90 56 65 74 68 86 99" a = "56 65 74 100 99 68 86 180 90",按数字排序权重变为:"100 180 90 56 65 74 68 86 99"

When two numbers have the same "weight", let us class them as if they were strings and not numbers: 100 is before 180 because its "weight" (1) is less than the one of 180 (9) and 180 is before 90 since, having the same "weight" (9) it comes before as a string.当两个数字具有相同的“权重”时,让我们将它们归类为字符串而不是数字:100 在 180 之前,因为它的“权重”(1) 小于 180 (9) 中的一个,而 180 在 90 之前因为,具有相同的“重量”(9),它作为字符串出现在前面。

All numbers in the list are positive numbers and the list can be empty.列表中的所有数字都是正数,列表可以为空。

My tests:我的测试:

[TestMethod]
public void Test1()
{
     Assert.AreEqual("2000 103 123 4444 99",
         WeightSort.orderWeight("103 123 4444 99 2000"));
}

[TestMethod]
public void Test2()
{
    Assert.AreEqual("11 11 2000 10003 22 123 1234000 44444444 9999",
        WeightSort.orderWeight("2000 10003 1234000 44444444 9999 11 11 22 123"));
}

My class to calculate the order of the weights:我的班级计算权重的顺序:

public class WeightSort
{
    public static string orderWeight(string strng)
    {
        List<int> list = strng.Split(' ').Select(Int32.Parse).OrderBy(i => i).ToList();
        List<int> SumofNums = new List<int>();
        List<string> SumandNums = new List<string>();
        List<string> SumandNums2 = new List<string>();
        List<string> Nums = new List<string>();

        foreach (var itm in list)
        {
            int num = (int)GetSumOfDigits(itm);
            SumofNums.Add(num);
            SumandNums.Add(itm + "," + num);
        }
        SumofNums = SumofNums.OrderBy(i => i).ToList();
        string txt = "";            
        foreach (var itm in SumofNums)
        {
            var item = itm.ToString();
            if (!Nums.Contains(item))
            {
                foreach (var itm2 in SumandNums)
                {
                    var itm3 = itm2.Split(',');
                    if (item == itm3[1])
                    {
                        SumandNums2.Add(itm2);
                        if (string.IsNullOrEmpty(txt))
                            txt = itm3[0];
                        else
                            txt = txt + " " + itm3[0];
                    }
                }
                Nums.Add(item);
            }            
        }

        return txt;
    }

    static long GetSumOfDigits(long n)
    {
        long num2 = 0;
        long num3 = n;
        long r = 0;
        while (num3 != 0)
        {
            r = num3 % 10;
            num3 = num3 / 10;
            num2 = num2 + r;
        }

        return num2;
    }
}

I can handle if there is only one but not duplicates.如果只有一个但没有重复,我可以处理。 Please help me rewrite my class so it can handle the duplicates also..请帮我重写我的课程,以便它也可以处理重复项..

Sum of digits:数字总和:

string weights = "103 123 4444 99 2000";

1) 2000, digit sum = 2;
2) 103, digit sum = 4;
3) 123, digit sum = 6;
4) 4444, digit sum = 16;
5) 99, digit sum = 18;

the correct order is "2000 103 123 4444 99"

You can use Linq if sorting by weight means如果按重量排序,您可以使用Linq

  • by sum of digits数字总和
  • lexicographically ("as strings")按字典顺序(“作为字符串”)

the implementation实施

  String a = "56 65 74 100 99 68 86 180 90";

  // 100 180 90 56 65 74 68 86 99
  String result = String.Join(" ", a
    .Split(' ')
    .OrderBy(item => item.Sum(ch => ch - '0')) // sum of digits
    .ThenBy(item => item));                    // lexicographic ("as string")

Try this:尝试这个:

var input = "103 123 4444 99 2000";
var sorted = input.Split(' ').OrderBy(s => s.Sum(c => c - '0')).ThenBy(s => s);
var result = string.Join(" ", sorted);

Addition: I realize now that Dmitry's answer had evolved into the same as mine before I posted mine.补充:我现在意识到德米特里的答案在我发布我的之前已经演变成和我的一样。

New addition: If you find that s.Sum(c => c - '0') is like a hack, you can be using System.Globalization;新增:如果你发现s.Sum(c => c - '0')就像一个黑客,你可以using System.Globalization; and say s.Sum((Func<char, int>)CharUnicodeInfo.GetDecimalDigitValue) instead.并说s.Sum((Func<char, int>)CharUnicodeInfo.GetDecimalDigitValue)代替。


You can validate in the lambda.您可以在 lambda 中进行验证。 For example:例如:

var sorted = input.Split(' ')
  .OrderBy(s => s.Sum(c => { if (c < '0' || c > '9') { throw new ArgumentOutOfRangeException("c", "Unexpected character."); } return c - '0'; }))
  .ThenBy(s => s);

You can also do this by creating a comparer which tells you whether a value is greater than or less than another value and can then be used.您也可以通过创建一个比较器来做到这一点,该比较器告诉您一个值是大于还是小于另一个值,然后可以使用。 The code largely speaks for itself:代码在很大程度上不言自明:

void Main()
{
    var strings = new List<string>("2000 10003 1234000 44444444 9999 11 11 22 123".Split(' '));
    strings.Sort(new MyComparer());
    Console.WriteLine(String.Join(" ", strings));
}

public class MyComparer : IComparer<string>
{
    public int Compare(string a, string b)
    {
        var aWeight = GetWeight(a);
        var bWeight = GetWeight(b);
        if (aWeight==bWeight)
        {
            return String.Compare(a,b);
        }
        else
        {
            return aWeight < bWeight ? -1 : 1;
        }

    }

    private int GetWeight(string number)
    {
        var weight = 0;
        foreach(var digit in number)
        {
            weight+=Int32.Parse(digit.ToString());
        }
        return weight;
    }
}

The key thing is the MyComparer class which defines a single public method that takes two values in. It gets the weights of the objects and if they are the same it falls back to string comparison.关键是MyComparer类,它定义了一个接受两个值的公共方法。它获取对象的权重,如果它们相同,则返回到字符串比较。

This comparer can then be passed to a sort function such as that of List<T> to then do the sorting.然后可以将此比较器传递给排序函数,例如List<T>的排序函数,然后进行排序。

This is much lengthier but I thought it worth sharing as it is a little more reusable (eg if you do this in a lot of places in your code you can have your logic in a single class) and it can sometimes be a bit more readable.这要长得多,但我认为值得分享,因为它更可重用(例如,如果你在代码的很多地方这样做,你可以在一个类中拥有你的逻辑),它有时会更易读.

I also note I am not a fan of ch - '0' as a way of getting the int value of a character since it is not always obvious at a glance what it does if you don't know the trick.我还注意到,我不喜欢ch - '0'作为获取字符 int 值的一种方式,因为如果您不知道诀窍,它的作用并不总是一目了然。 Also in the event of non numeric characters it will still do things, just not necessarily anything sensible.同样在非数字字符的情况下,它仍然会做一些事情,只是不一定是任何明智的事情。 Mine will just throw a good old fashioned exception that can be caught if you pass it any non-numeric data.我的只会抛出一个很好的老式异常,如果您向它传递任何非数字数据,则可以捕获该异常。

I found this solution pretty short and clear:我发现这个解决方案非常简短明了:

var orderedNumbers = "56 65 74 100 99 68 86 180 90".Split(' ')
    .OrderBy(GetWeight)
    .ThenBy(x => x);
var result = String.Join(" ", orderedNumbers);

This will first calculate the weight from any given number and sort by this value.这将首先计算任何给定数字的权重并按此值排序。 If it´s equal the ThenBy -clause comes to the play and furtherly orders the result by performing a string-comparison (as the values returned by the first OrderBy is a list of strings).如果它等于ThenBy子句,并通过执行字符串比较进一步对结果进行排序(因为第一个OrderBy返回的值是一个字符串列表)。

With

int GetWeight(string number)
{
    return number.Sum(x => CharUnicodeInfo.GetDecimalDigitValue(x));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM