Home > Back-end >  Math Comparison on 2 Numbers with Multiple Decimals
Math Comparison on 2 Numbers with Multiple Decimals

Time:10-24

I need to compare section numbers in a document, at first I was just going to convert to decimal and check to see if one number is greater than another. The issue there is that some sections have multiple decimals.

Example: I need to perform a math comparison on 1.1 with 1.1.2.3 to determine which one is farther along in a document.

They are strings to begin with and I need to do some math comparisons on them essentially. I though about removing the decimals then converting to int but this throws off certain sections, like section 2 would be considered less than section 1.1 since 1.1 would be changed to a 11, which is no good.

    string section1 = "1.1";
    string section2 = "2";
    int convertedSection1 = Convert.ToInt32(section1.Replace(".",""));
    int convertedSection2 = Convert.ToInt32(section2.Replace(".",""));
    if(convertedSection1 < convertedSection2)
        //This will incorrectly say 1.1 is greater than 2

    string section1 = "1.1.2.4";
    string section2 = "2.4";
    decimal convertedSection1 = Convert.ToDecimal(section1);
    decimal convertedSection2 = Convert.ToDecimal(section2);
    if(convertedSection1 < convertedSection2)
        //This will convert 1.1.2.4 to 1.1 which is no good

CodePudding user response:

You can create a class similar to the Version class of the .NET framework. If you implement some operators and IComparable, it's really nice.

How does it work? It will convert the given string into a list of integer numbers. Upon comparison, it will start at the beginning of each list and compare each individual part.

public class Section: IComparable<Section>
{
    // Stores all individual components of the section
    private List<int> parts = new List<int>();

    // Construct a section from a string
    public Section(string section)
    {
        var strings = section.Split('.');
        foreach (var s in strings)
        {
            parts.Add(int.Parse(s));
        }
    }

    // Make it nice for display
    public override string ToString()
    {
        return  string.Join(".", parts);
    }

    // Implement comparison operators for convenience
    public static bool operator ==(Section a, Section b)
    {
        // Comparing the identical object
        if (ReferenceEquals(a, b)) return true;

        // One object is null and the other isn't
        if ((object)a == null) return false;
        if ((object)b == null) return false;

        // Different amount of items
        if (a.parts.Count != b.parts.Count) return false;

        // Check all individual items
        for (int i=0; i<a.parts.Count;i  )
        {
            if (a.parts[i] != b.parts[i]) return false;
        }

        return true;
    }
    public static bool operator !=(Section a, Section b)
    {
        return !(a == b);
    }

    public static bool operator <(Section a, Section b)
    {
        // Use minimum, otherwise we exceed the index
        for (int i=0; i< Math.Min(a.parts.Count, b.parts.Count); i  )
        {
            if (a.parts[i] < b.parts[i]) return true;
        }

        if (b.parts.Count > a.parts.Count) return true;
        return false;
    }

    public static bool operator >(Section a, Section b)
    {
        // Use minimum, otherwise we exceed the index
        for (int i = 0; i < Math.Min(a.parts.Count, b.parts.Count); i  )
        {
            if (a.parts[i] > b.parts[i]) return true;
        }

        if (a.parts.Count > b.parts.Count) return true;
        return false;
    }

    // Implement the IComparable interface for sorting
    public int CompareTo(Section other)
    {
        if (this == other) return 0;
        if (this < other) return -1;
        return 1;
    }
}

Tests for 96% coverage:

Assert.IsTrue(new Section("1.2.3.4") > new Section("1.2.3"));
Assert.IsFalse(new Section("1.2.3.4") < new Section("1.2.3"));
Assert.IsFalse(new Section("1.2.3.4") == new Section("1.2.3"));
Assert.IsTrue(new Section("1.2.3.4") == new Section("1.2.3.4"));
Assert.IsFalse(new Section("1.2.3.4") == new Section("1.2.3.5"));
Assert.IsTrue(new Section("1.2.3.4") != new Section("1.2.3.5"));
var sec = new Section("1");
Assert.IsTrue(sec == sec);

Assert.AreEqual("1.2.3.4", new Section("1.2.3.4").ToString());

var sortTest = new List<Section> { new Section("2"), new Section("1.2"), new Section("1"), new Section("3.1") };
sortTest.Sort();
var expected = new List<Section> { new Section("1"), new Section("1.2"), new Section("2"), new Section("3.1") };
CollectionAssert.AreEqual(expected, sortTest, new SectionComparer());

CodePudding user response:

If you know that your section strings are always well formed, and you know that they don't go deeper than 6 levels, and that no level has more than 999 items, then this works nicely:

string zero = ".0.0.0.0.0.0";
long Section2Long(string section) =>
    (section   zero)
        .Split('.')
        .Take(6)
        .Select(t => long.Parse(t))
        .Aggregate((x, y) => x * 1000   y);

Now, if I have this:

string[] sections = new []
{
    "1.2.4", "2.3", "1", "1.2", "1.1.1.1", "1.0.0.1.0.1", "2.2.9"
};

I can easily sort it like this:

string[] sorted = sections.OrderBy(x => Section2Long(x)).ToArray();

I get this output:

1 
1.0.0.1.0.1 
1.1.1.1 
1.2 
1.2.4 
2.2.9 
2.3 
  •  Tags:  
  • c#
  • Related