Linq/Lambda OrderBy Delegate for List of IP Addresses

前端 未结 6 632
粉色の甜心
粉色の甜心 2021-01-01 21:34

Given List ips = new List();

I need to sort the list of IP addresses in a logical order (i.e. \"192.168.0.2\" comes before \

相关标签:
6条回答
  • 2021-01-01 22:12

    You could split this into 4 integer values, and sort by each in turn:

     var results = ips
           .Select(s => string.Split('.').Select(str => int.Parse(str)).ToArray() )
           .OrderBy(intArray => intArray[0])
           .ThenBy(intArray => intArray[1])
           .ThenBy(intArray => intArray[2])
           .ThenBy(intArray => intArray[3])
           .Select(intArray => string.Join(".", intArray) );
    
    0 讨论(0)
  • 2021-01-01 22:20

    I would create a comparer for System.Net.IPAddress like so

    class IPAddressComparer : IComparer<IPAddress> {
        public int Compare(IPAddress x, IPAddress y) {
            byte[] first = x.GetAddressBytes();
            byte[] second = y.GetAddressBytes();
            return first.Zip(second, (a, b) => a.CompareTo(b))
                        .FirstOrDefault(c => c != 0);
        }   
    }
    

    and then proceed as follows:

    var list = new List<string>() {
        "192.168.0.1",
        "192.168.0.10",
        "192.168.0.2",
        "192.168.0.200"
    };
    var sorted = list.OrderBy(s => IPAddress.Parse(s), new IPAddressComparer());
    
    0 讨论(0)
  • 2021-01-01 22:24

    You need to make a comparer: (Tested)

    class IPComparer : IComparer<string> {
        public int Compare(string a, string b) {
            return Enumerable.Zip(a.Split('.'), b.Split('.'), 
                                 (x, y) => int.Parse(x).CompareTo(int.Parse(y)))
                             .FirstOrDefault(i => i != 0); 
        }
    }
    

    You can then write

    ips.OrderBy(p => p, new IPComparer()) 
    
    0 讨论(0)
  • 2021-01-01 22:29

    This is an old question but I was looking up IP Comparer's and it came up. I wanted something that worked for IPv6 as well though so once I got it I thought I'd add it here for the next person who does a search for it. Much like SLaks's answer, I agree that an IComparer is probably best.

    public class IPComparer : IComparer<IPAddress>
    {
        public int Compare(IPAddress x, IPAddress y)
        {
            if (ReferenceEquals(x, null))
                throw new ArgumentNullException("x");
    
            if (ReferenceEquals(y, null))
                throw new ArgumentNullException("y");
    
            return BitConverter.ToUInt32(x.GetAddressBytes().Reverse().ToArray(),0)
                .CompareTo(BitConverter.ToUInt32(y.GetAddressBytes().Reverse().ToArray(),0));
        }
    }
    

    Nothing fancy but it should work.

    0 讨论(0)
  • 2021-01-01 22:30

    I wrote an IpComparer for IPv6. The variant from Howel doesn't work.

    Here is the Comparer:

    /// <summary>
    /// Compares two ip addresses.
    /// http://stackoverflow.com/questions/4785218/linq-lambda-orderby-delegate-for-liststring-of-ip-addresses
    /// </summary>
    public class IpComparer : IComparer<IPAddress>
    {
        /// <summary>
        /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
        /// </summary>
        /// 
        /// <returns>
        /// A signed integer that indicates the relative values of <paramref name="x"/> and <paramref name="y"/>, as shown in the following table.
        /// Value Meaning Less than zero<paramref name="x"/> is less than <paramref name="y"/>.
        /// Zero<paramref name="x"/> equals <paramref name="y"/>.
        /// Greater than zero <paramref name="x"/> is greater than <paramref name="y"/>.
        /// </returns>
        /// <param name="x">The first object to compare.</param><param name="y">The second object to compare.</param>
        public int Compare(IPAddress x, IPAddress y)
        {
            if (ReferenceEquals(x, null))
            {
                throw new ArgumentNullException("x");
            }
    
            if (ReferenceEquals(y, null))
            {
                throw new ArgumentNullException("y");
            }
    
            byte[] bytesOfX = x.GetAddressBytes();
            byte[] bytesOfY = y.GetAddressBytes();
    
            return StructuralComparisons.StructuralComparer.Compare(bytesOfX, bytesOfY);
        }
    }
    

    And here a unit test:

    [TestFixture]
    public class IpComparerTest : AbstractUnitTest
    {
        private IpComparer _testee;
    
        [SetUp]
        public void Setup()
        {
            _testee = new IpComparer();
        }
    
        [TestCase("10.156.35.205", "10.156.35.205")]
        [TestCase("0.0.0.1", "0.0.0.1")]
        [TestCase("2001:0db8:0000:08d3:0000:8a2e:0070:7344", "2001:db8:0:8d3:0:8a2e:70:7344")]
        [TestCase("2001:0db8:0:0:0:0:1428:57ab", "2001:db8::1428:57ab")]
        [TestCase("2001:0db8:0:0:8d3:0:0:0", "2001:db8:0:0:8d3::")]
        [TestCase("::ffff:127.0.0.1", "::ffff:7f00:1")]
        public void Compare_WhenIpsAreEqual_ThenResultIsZero(string ip1, string ip2)
        {
            // Arrange
            IPAddress x = IPAddress.Parse(ip1);
            IPAddress y = IPAddress.Parse(ip2);
    
            // Act and Assert
            Assert.That(_testee.Compare(x, y), Is.EqualTo(0));
        }
    
        [TestCase("10.156.35.2", "10.156.35.205")]
        [TestCase("0.0.0.0", "0.0.0.1")]
        [TestCase("1001:0db8:85a3:08d3:1319:8a2e:0370:7344", "2001:0db8:85a3:08d3:1319:8a2e:0370:7344")]
        [TestCase("2001:0db8:85a3:08d3:1319:8a2e:0370:7343", "2001:0db8:85a3:08d3:1319:8a2e:0370:7344")]
        public void Compare_WhenIp1IsLessThanIp2_ThenResultIsLessThanZero(string ip1, string ip2)
        {
            // Arrange
            IPAddress x = IPAddress.Parse(ip1);
            IPAddress y = IPAddress.Parse(ip2);
    
            // Act and Assert
            Assert.That(_testee.Compare(x, y), Is.LessThan(0));
        }
    
        [TestCase("10.156.35.205", "10.156.35.2")]
        [TestCase("0.0.0.1", "0.0.0.0")]
        [TestCase("3001:0db8:85a3:08d3:1319:8a2e:0370:7344", "2001:0db8:85a3:08d3:1319:8a2e:0370:7344")]
        [TestCase("2001:0db8:85a3:08d3:1319:8a2e:0370:7345", "2001:0db8:85a3:08d3:1319:8a2e:0370:7344")]
        public void Compare_WhenIp1IsGreaterThanIp2_ThenResultIsGreaterThanZero(string ip1, string ip2)
        {
            // Arrange
            IPAddress x = IPAddress.Parse(ip1);
            IPAddress y = IPAddress.Parse(ip2);
    
            // Act and Assert
            Assert.That(_testee.Compare(x, y), Is.GreaterThan(0));
        }
    }
    

    I hope this solution is correct. I'm not an expert in IPv6.

    0 讨论(0)
  • 2021-01-01 22:32

    This one is pretty elegant (and fail proof if you use TryParse):

    var sorted2 = from ip in ips
                  let addressBytes = IPAddress.Parse(ip).GetAddressBytes()
                  orderby addressBytes[0], addressBytes[1], addressBytes[2], addressBytes[3]
                  select ip;
    

    The addressBytes array will have length 4 as long as it is only IP4 addresses. Otherwise you should account for the length...

    0 讨论(0)
提交回复
热议问题