2011-01-24 16 views
14

Biorąc List<string> ips = new List<string>();LINQ/Lambda OrderBy Delegat listy <string> adresów IP

muszę uporządkować listę adresów IP w porządku logicznym (czyli „192.168.0.2” jest przed „192.168.0.100”).

Obecnie (i słusznie, alfabetycznie) jeśli lista zawiera:

192.168.0.1 
192.168.0.2 
192.168.0.10 
192.168.0.200 

ips.OrderBy(p => p) powraca:

192.168.0.1 
192.168.0.10 
192.168.0.2 
192.168.0.200 
+0

Kolejne bardzo sprytne rozwiązanie na stronie: http://stackoverflow.com/questions/6248039/how-to-sort-list-of-ip-addresses-using-c/6248074#6248074 –

Odpowiedz

17

Trzeba dokonać porównywarka: (testowane)

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

Następnie możesz napisać:

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

Dlaczego zostało to odrzucone? – SLaks

+0

Bardzo sprytne rozwiązanie. –

+0

Wow - sprytnie. Nie wiem, dlaczego został wycofany. Oto kolejka do skompensowania :) –

2

Można podzielić to na 4 liczb całkowitych i sortować według każdego z kolei:

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

To będzie usuń wiodące zera z oktetów.(Które mogą lub nie mogą być pożądane) – SLaks

+0

@SLaks: True - może, ale nie musi mieć znaczenia. Możesz w razie potrzeby zwrócić oryginalny ciąg znaków, ale byłoby to mniej zrozumiałe, aby go przekazać ... –

+0

@SLaks: Jeśli byłyby wiodące zera, oryginalny problem z .OrderBy() nie istniałby - z natury uporządkowaliby poprawnie. To rozwiązanie zadziałałoby, ale program IComparer jest bardziej elegancki i dlatego został wybrany jako odpowiedź. – lukiffer

2

ten jest bardzo elegancki (a nie dowód, jeśli używasz TryParse):

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

Macierz addressBytes będzie mieć długość 4, o ile są to tylko adresy IP4. W przeciwnym razie należy uwzględnić długość ...

6

Chciałbym utworzyć Comparer dla System.Net.IPAddress jak tak

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

a następnie postępować w następujący sposób:

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

Nice. Prosty i obsługuje IPv6. – gregmac

0

To stara sprawa, ale ja szukał IP porównywarki i pojawił się. Chciałem też czegoś, co działało na IPv6, więc kiedy je otrzymałem, pomyślałem, że dodam go tutaj dla następnej osoby, która szuka. Podobnie jak odpowiedź SLaksa, zgadzam się, że IComparer jest prawdopodobnie najlepszy.

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

Nic nadzwyczajnego, ale powinno działać.

1

Napisałem IpComparer dla IPv6. Wariant z Howel nie działa.

Oto porównywarka:

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

I tu badanej jednostki:

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

Mam nadzieję, że to rozwiązanie jest prawidłowe. Nie jestem ekspertem od IPv6.

Powiązane problemy