2011-01-24 26 views
14

Với List<string> ips = new List<string>();LINQ/Lambda OrderBy Đại biểu cho Danh sách <string> của IP Addresses

tôi cần phải sắp xếp danh sách các địa chỉ IP theo một thứ tự hợp lý (ví dụ: "192.168.0.2" đi trước "192.168.0.100").

Hiện nay (và chính xác, theo thứ tự abc) nếu danh sách chứa:

192.168.0.1 
192.168.0.2 
192.168.0.10 
192.168.0.200 

ips.OrderBy(p => p) lợi nhuận:

192.168.0.1 
192.168.0.10 
192.168.0.2 
192.168.0.200 
+0

Một giải pháp rất thông minh khác trên SO: http://stackoverflow.com/questions/6248039/how-to-sort-list-of-ip-addresses-using-c/6248074#6248074 –

Trả lời

17

Bạn cần phải thực hiện một 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); 
    } 
} 

Sau đó, bạn có thể viết

ips.OrderBy(p => p, new IPComparer()) 
+0

Tại sao điều này lại được bình chọn? – SLaks

+0

Giải pháp rất thông minh. –

+0

Wow - thực sự thông minh. Không chắc chắn lý do tại sao nó đã được downvoted. Dưới đây là một upvote để bù đắp :) –

2

Bạn có thể chia này thành 4 giá trị số nguyên, và sắp xếp theo từng lần lượt:

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

Điều này sẽ dải zeroes hàng đầu từ các octet.(Có thể hoặc không mong muốn) – SLaks

+0

@SLaks: Đúng - có thể hoặc có thể không quan trọng. Bạn có thể làm cho nó trả về chuỗi ban đầu, nếu cần thiết, nhưng nó sẽ là một chút ít dễ hiểu để vượt qua nó thông qua ... –

+0

@SLaks: Nếu các số 0 ở đầu hiện diện, vấn đề ban đầu với .OrderBy() sẽ không tồn tại - họ vốn đã đặt hàng một cách chính xác. Giải pháp này sẽ hoạt động, nhưng IComparer thanh lịch hơn và do đó được chọn làm câu trả lời. – lukiffer

2

Cái này là khá tao nhã (và thất bại bằng chứng nếu bạn sử dụng TryParse):

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

Mảng addressBytes sẽ có độ dài 4 miễn là nó chỉ là địa chỉ IP4. Nếu không, bạn nên giải thích cho độ dài ...

6

tôi sẽ tạo ra một Comparer cho System.Net.IPAddress như vậy

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

và sau đó tiến hành như sau:

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

Rất đẹp. Đơn giản và hỗ trợ IPv6. – gregmac

0

Đây là một câu hỏi cũ nhưng tôi đang tìm kiếm IP Comparer và nó xuất hiện. Tôi muốn một cái gì đó mà làm việc cho IPv6 cũng như vậy một khi tôi đã nhận nó tôi nghĩ rằng tôi muốn thêm nó ở đây cho người tiếp theo người thực hiện một tìm kiếm cho nó. Giống như câu trả lời của SLaks, tôi đồng ý rằng một IComparer có lẽ là tốt nhất.

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

Không có gì lạ mắt nhưng nó sẽ hoạt động.

1

Tôi đã viết một IpComparer cho IPv6. Biến thể từ Howel không hoạt động.

Đây là 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); 
    } 
} 

Và đây là một thử nghiệm đơn vị:

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

tôi hy vọng giải pháp này là đúng. Tôi không phải là chuyên gia về IPv6.

Các vấn đề liên quan