How to sort IP addresses stored in dictionary in Python?
Solution 1
You can use a custom key
function to return a sortable representation of your strings:
def split_ip(ip):
"""Split a IP address given as string into a 4-tuple of integers."""
return tuple(int(part) for part in ip.split('.'))
def my_key(item):
return split_ip(item[0])
items = sorted(ipCount.items(), key=my_key)
The split_ip()
function takes an IP address string like '192.168.102.105'
and turns it into a tuple of integers (192, 168, 102, 105)
. Python has built-in support to sort tuples lexicographically.
UPDATE: This can actually be done even easier using the inet_aton()
function in the socket
module:
import socket
items = sorted(ipCount.items(), key=lambda item: socket.inet_aton(item[0]))
Solution 2
A clean way of handling the right order is using Pythons ipaddress module. You can transform the Strings into IPv4Address representations and sort them afterwards. Here's a working example with list objects (Tested with Python3):
import ipaddress
unsorted_list = [
'192.168.102.105',
'192.168.204.111',
'192.168.99.11'
]
new_list = []
for element in unsorted_list:
new_list.append(ipaddress.ip_address(element))
new_list.sort()
# [IPv4Address('192.168.99.11'), IPv4Address('192.168.102.105'), IPv4Address('192.168.204.111')]
print(new_list)
Solution 3
Found a solution at https://www.lesinskis.com/python_sorting_IP_addresses.html All you have to do is convert the string of ip in ipaddress
import ipaddress
sortedkey = sorted(list_of_ip_instring, key = ipaddress.IPv4Address)
Solution 4
if your application does lots of things like "find ips in range x", "sort by ip" etc its often more convenient to store the numeric value of the ip internally and work with this one.
from socket import inet_aton,inet_ntoa
import struct
def ip2long(ip):
packed = inet_aton(ip)
lng = struct.unpack("!L", packed)[0]
return lng
convert the number back into an ip using this function:
def long2ip(lng):
packed = struct.pack("!L", lng)
ip=inet_ntoa(packed)
return ip
>>> ip2long('192.168.1.1')
3232235777
>>> ip2long('1.2.3.4')
16909060
>>> long2ip(3232235777)
'192.168.1.1'
>>> long2ip(16909060)
'1.2.3.4'
Solution 5
What are my options here?
The two obvious one that come to my mind are:
- Preformatting the strings with the IP when you store them as from the link you put in your question.
-
Pass a sorting function to
sorted()
function when you perform the ordering.
Which is best depends from the amount of data you have to process (you will notice an increased performance for method #1 only for very large amount of data) and from what you will need to do with said sorted list of IP (if you preformat the strings, you might then need to change them again before feeding them as arguments to other functions, for example).
Example of preformatting
Maintain the IP as a string, but uses spaces or zeroes to solve the variable number of digits problem:
>>> ip = '192.168.1.1'
>>> print('%3s.%3s.%3s.%3s' % tuple(ip.split('.')))
192.168. 1. 1
>>> print('%s.%s.%s.%s' % tuple([s.zfill(3) for s in ip.split('.')]))
192.168.001.001
Example of sorting function
Well... Ferdinand Beyer in his answer seems to have already offered an excellent solution for this approach! :)
Comments
-
tkit almost 4 years
I have a piece of code that looks like this:
ipCount = defaultdict(int) for logLine in logLines: date, serverIp, clientIp = logLine.split(" ") ipCount[clientIp] += 1 for clientIp, hitCount in sorted(ipCount.items), key=operator.itemgetter(0)): print(clientIp)
and it kind of sorts IP's, but like this:
192.168.102.105 192.168.204.111 192.168.99.11
which is not good enough since it does not recognize that 99 is a smaller number than 102 or 204. I would like the output to be like this:
192.168.99.11 192.168.102.105 192.168.204.111
I found this, but I am not sure how to implement it in my code, or if it is even possible since I use dictionary. What are my options here? Thank you..
-
Ferdinand Beyer almost 13 years+1 for recommending the
socket
module. But since the question is about sorting, you might want to give an example on how to use it in this context. -
Handyman5 over 12 yearsYou can also do this with the
map
function:sorted(ipCount.items(), key=lambda x:tuple(map(int, x.split('.')))))
-
Rafael Barbosa over 11 yearsI see the updated answers using inet_aton are different: Ludo's call struct.unpack and Ferdinand's does not. Is this function call necessary for ordering or not?
-
Ferdinand Beyer over 11 years@randomtoor: It is not necessary.
inet_aton
returns a string of 4 characters, and Python knows how to compare and sort strings. -
ThorSummoner over 8 yearsThe Updated version is awesome! Should be the primary version & answer IMO.