0

I have a list of versions that I need to sort semantically using Strict Version library in Python. The problem is that there are two strings in the list: 'Unknown' and 'Not A Version' and when I run the code they cause error. Here is the list

ver_list = ['Unknown' 'Not GAP Version' '4.9.3' '4.9.2' '4.9.1' '4.9.0' '4.9' '4.8.9'
 '4.8.8' '4.8.7' '4.8.6' '4.8.5' '4.8.4' '4.8.3' '4.8.2' '4.8.10' '4.8.1'
 '4.8' '4.7.9' '4.7.8' '4.7.7' '4.7.6' '4.7.5' '4.7.4' '4.7.2' '4.7'
 '4.6.9' '4.6.5' '4.6.4' '4.6.3' '4.6.2' '4.6.12' '4.6.1' '4.6' '4.5.7'
 '4.5.6' '4.5.5' '4.5.4' '4.5.3' '4.5' '4.49' '4.46' '4.4.9' '4.4.7'
 '4.4.6' '4.4.5' '4.4.4' '4.4.3' '4.4.2' '4.4.12' '4.4.11' '4.4.10' '4.4'
 '4.3' '4.2' '4.11.0' '4.11' '4.10.2' '4.10.1' '4.10.0' '4.10' '4.1'
 '3.4.4' '3.4.3' '3.4' '3.3' '3.2' '3.1' '3.0' '1.1' '1.0']

Here is the code:

ver_list = ver_list.sort(key=StrictVersion)

The error message is ValueError 'Unknown' is not a valid version number...

I also tried to convert the list to pandas dataframe series and used the following code from here How can i sort semantic versions in pandas? but I got the same error message, here is the code to use when versions are your index:

ver = ver.reindex(index=pd.Index(sorted(ver.index, key=StrictVersion)))

I simply need to sort them semantically but the final result to also contain the 'Unknown' and the other string, regardless if they are at the start or at the end. Thank you for the help, much appreciated.

3 Answers3

1

Bit of a hacky solution, but I would implement the check that the StrictVersion parse method (see GitHub link) uses to throw the error before the method is used and manually exclude these. Then combine them at the end. You could also add a check to see if all elements of the exception_list were expected, i.e. Unknown and Not GAP Version in this case.

from distutils.version import StrictVersion
import re

ver_list = ['Unknown', 'Not GAP Version', '4.9.3', '4.9.2', '4.9.1', '4.9.0', '4.9', '4.8.9',
'4.8.8', '4.8.7', '4.8.6', '4.8.5', '4.8.4', '4.8.3', '4.8.2', '4.8.10', '4.8.1',
'4.8', '4.7.9', '4.7.8', '4.7.7', '4.7.6', '4.7.5', '4.7.4', '4.7.2', '4.7',
'4.6.9', '4.6.5', '4.6.4', '4.6.3', '4.6.2', '4.6.12', '4.6.1', '4.6', '4.5.7',
'4.5.6', '4.5.5', '4.5.4', '4.5.3', '4.5', '4.49', '4.46', '4.4.9', '4.4.7',
'4.4.6', '4.4.5', '4.4.4', '4.4.3', '4.4.2', '4.4.12', '4.4.11', '4.4.10', '4.4',
'4.3', '4.2', '4.11.0', '4.11', '4.10.2', '4.10.1', '4.10.0', '4.10', '4.1',
'3.4.4', '3.4.3', '3.4', '3.3', '3.2', '3.1', '3.0', '1.1', '1.0']

exception_list = []

version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
                            re.VERBOSE | re.ASCII)
for ver in ver_list:
    match = version_re.match(ver)
    if not match:
        exception_list.append(ver)
for i in exception_list:
    ver_list.remove(i)

ver_list.sort(key=StrictVersion)

result = exception_list + ver_list

Which gives the result

['Unknown', 'Not GAP Version', '1.0', '1.1', '3.0', '3.1', '3.2', '3.3', '3.4', '3.4.3', '3.4.4', '4.1', '4.2', '4.3', '4.4', '4.4.2', '4.4.3', '4.4.4', '4.4.5', '4.4.6', '4.4.7', '4.4.9', '4.4.10', '4.4.11', '4.4.12', '4.5', '4.5.3', '4.5.4', '4.5.5', '4.5.6', '4.5.7', '4.6', '4.6.1', '4.6.2', '4.6.3', '4.6.4', '4.6.5', '4.6.9', '4.6.12', '4.7', '4.7.2', '4.7.4', '4.7.5', '4.7.6', '4.7.7', '4.7.8', '4.7.9', '4.8', '4.8.1', '4.8.2', '4.8.3', '4.8.4', '4.8.5', '4.8.6', '4.8.7', '4.8.8', '4.8.9', '4.8.10', '4.9.0', '4.9', '4.9.1', '4.9.2', '4.9.3', '4.10.0', '4.10', '4.10.1', '4.10.2', '4.11.0', '4.11', '4.46', '4.49']
ChrisOram
  • 1,254
  • 1
  • 5
  • 17
1

Since ver_list is a list of strings, you can do this workaround

Remove those two strings - Unknown and Not GAP Verison from ver_list.

ver_list.remove('Unknown')
ver_list.remove('Not GAP Version')

Sort ver_list based on Version numbers

ver_list.sort(key=StrictVersion)

Add those removed strings back to the ver_list.

arr = ['Unknown', 'Not GAP Version'] 
  • Adding them at the end
ver_list = ver_list + arr
  • Adding them at the beginning
ver_list = arr + ver_list

Here is the Code:

from distutils.version import StrictVersion
ver_list = ['Unknown', 'Not GAP Version', '4.9.3', '4.9.2', '4.9.1', '4.9.0', '4.9', '4.8.9','4.8.8', '4.8.7', '4.8.6', '4.8.5','4.8.4','1.10.0', '4.10', '4.1','3.4.4', '3.4.3', '3.4', '3.3', '3.2', '3.1', '13.0', '1.1', '1.0']

# Removing the strings
ver_list.remove('Unknown')
ver_list.remove('Not GAP Version')

# List of removed strings
arr = ['Unknown', 'Not GAP Version']

# Sorting the ver_list
ver_list.sort(key=StrictVersion)

# Adding the removed strings back at the end of ver_list
ver_list = ver_list + arr

print(ver_list)
Output:

['1.0', '1.1', '1.10.0', '3.1', '3.2', '3.3', '3.4', '3.4.3', '3.4.4', '4.1', '4.8.4', '4.8.5', '4.8.6', '4.8.7', '4.8.8', '4.8.9', '4.9.0', '4.9', '4.9.1', '4.9.2', '4.9.3', '4.10', '13.0', 'Unknown', 'Not GAP Version']
Ram
  • 4,724
  • 2
  • 14
  • 22
1

Since the implementation of StrictVersion uses tuple order for comparisons, you can create a wrapper function around StrictVersion that returns an empty tuple when an exception is caught:

def LooserStrictVersion(version):
    try:
        return StrictVersion(version)
    except ValueError:
        return ()

so that you can sort the list with:

ver_list.sort(key=LooserStrictVersion)
blhsing
  • 91,368
  • 6
  • 71
  • 106