Sort a list of object identifiers

From Rosetta Code
Task
Sort a list of object identifiers
You are encouraged to solve this task according to the task description, using any language you may know.

Sorting Algorithm
This is a sorting algorithm. It may be applied to a set of data in order to sort it.

For other sorting algorithms, see Category:Sorting Algorithms, or:
O(n logn) Sorts
Heapsort | Mergesort | Quicksort
O(n log2n) Sorts
Shell Sort
O(n2) Sorts
Bubble sort | Cocktail sort | Comb sort | Gnome sort | Insertion sort | Selection sort | Strand sort
Other Sorts
Bead sort | Bogosort | Counting sort | Pancake sort | Permutation sort | Radix sort | Sleep sort | Stooge sort | Sort three variables
Object identifiers (OID) are strings used to identify objects in network data.


Task

Show how to sort a list of OIDs, in their natural sort order.

Details
  • An OID consists of one or more non-negative integers in base 10, separated by dots. It starts and ends with a number.
  • Their natural sort order is lexicographical with regard to the dot-separated fields, using numeric comparison between fields.
Test case
Input (list of strings) Output (list of strings)

1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0

1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1


C#[edit]

using System;
using System.Linq;
using System.Collections.Generic;
 
public class Program
{
public static void Main() {
var oids = new [] {
"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0"
};
 
var comparer = Comparer<string>.Create((a, b) => {
int c = a.Split('.').Select(int.Parse)
.Zip(b.Split('.').Select(int.Parse),
(i, j) => i.CompareTo(j)).FirstOrDefault(x => x != 0);
return c != 0 ? c : a.Length.CompareTo(b.Length);
});
 
Array.Sort(oids, comparer);
 
Console.WriteLine(string.Join(Environment.NewLine, oids));
}
}
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

C++[edit]

#include <string>
#include <vector>
#include <algorithm>
#include <boost/tokenizer.hpp>
#include <iostream>
 
std::vector<std::string> splitOnChar ( std::string & s , const char c ) {
typedef boost::tokenizer<boost::char_separator<char>> tokenizer ;
std::vector<std::string> parts ;
boost::char_separator<char> sep( &c ) ;
tokenizer tokens( s , sep ) ;
for ( auto it = tokens.begin( ) ; it != tokens.end( ) ; it++ )
parts.push_back( *it ) ;
return parts ;
}
 
bool myCompare ( const std::string & s1 , const std::string & s2 ) {
std::string firstcopy( s1 ) ;
std::string secondcopy ( s2 ) ;
std::vector<std::string> firstparts( splitOnChar ( firstcopy, '.' ) ) ;
std::vector<std::string> secondparts( splitOnChar ( secondcopy, '.' ) ) ;
std::vector<int> numbers1( firstparts.size( ) ) ;
std::vector<int> numbers2( secondparts.size( ) ) ;
std::transform( firstparts.begin( ) , firstparts.end( ) , numbers1.begin( ) ,
[]( std::string st ) { return std::stoi( st , nullptr ) ; } ) ;
std::transform( secondparts.begin( ) , secondparts.end( ) , numbers2.begin( ) ,
[]( std::string st ) { return std::stoi( st , nullptr ) ; } ) ;
auto it1 = numbers1.begin( ) ;
auto it2 = numbers2.begin( ) ;
while ( *it1 == *it2 ) {
it1++ ;
it2++ ;
}
if ( it1 == numbers1.end( ) || it2 == numbers2.end( ) )
return std::lexicographical_compare( s1.begin( ) , s1.end( ) , s2.begin( ) , s2.end( ) ) ;
return *it1 < *it2 ;
}
 
int main( ) {
std::vector<std::string> arrayOID { "1.3.6.1.4.1.11.2.17.19.3.4.0.10" ,
"1.3.6.1.4.1.11.2.17.5.2.0.79" ,
"1.3.6.1.4.1.11.2.17.19.3.4.0.4" ,
"1.3.6.1.4.1.11150.3.4.0.1" ,
"1.3.6.1.4.1.11.2.17.19.3.4.0.1" ,
"1.3.6.1.4.1.11150.3.4.0" } ;
std::sort( arrayOID.begin( ) , arrayOID.end( ) , myCompare ) ;
for ( std::string s : arrayOID )
std::cout << s << '\n' ;
return 0 ;
}
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Common Lisp[edit]

(defun oid->list (oid)
(loop for start = 0 then (1+ pos)
for pos = (position #\. oid :start start)
collect (parse-integer oid :start start :end pos)
while pos))
 
(defun list< (list1 list2)
(loop for e1 in list1
for e2 in list2
do (cond ((< e1 e2)
(return t))
((> e1 e2)
(return nil)))
finally (return (< (length list1) (length list2)))))
 
(defun sort-oids (oids)
(sort oids #'list< :key #'oid->list))
 
(defun main ()
(let ((oids (list "1.3.6.1.4.1.11.2.17.19.3.4.0.10"
"1.3.6.1.4.1.11.2.17.5.2.0.79"
"1.3.6.1.4.1.11.2.17.19.3.4.0.4"
"1.3.6.1.4.1.11150.3.4.0.1"
"1.3.6.1.4.1.11.2.17.19.3.4.0.1"
"1.3.6.1.4.1.11150.3.4.0")))
(dolist (oid (sort-oids oids))
(write-line oid))))
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Elixir[edit]

defmodule Sort_by_OID do
def numbers(list) do
Enum.sort_by(list, fn oid ->
String.split(oid, ".") |> Enum.map(&String.to_integer/1)
end)
end
end
 
~w[
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
]
|> Sort_by_OID.numbers
|> Enum.each(fn oid -> IO.puts oid end)
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Go[edit]

package main
 
import (
"fmt"
"log"
"math/big"
"sort"
"strings"
)
 
var testCases = []string{
"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0",
}
 
// a parsed representation
type oid []big.Int
 
// "constructor" parses string representation
func newOid(s string) oid {
ns := strings.Split(s, ".")
os := make(oid, len(ns))
for i, n := range ns {
if _, ok := os[i].SetString(n, 10); !ok || os[i].Sign() < 0 {
return nil
}
}
return os
}
 
// "stringer" formats into string representation
func (o oid) String() string {
s := make([]string, len(o))
for i, n := range o {
s[i] = n.String()
}
return strings.Join(s, ".")
}
 
func main() {
// parse test cases
os := make([]oid, len(testCases))
for i, s := range testCases {
os[i] = newOid(s)
if os[i] == nil {
log.Fatal("invalid OID")
}
}
// sort
sort.Slice(os, func(i, j int) bool {
// "less" function must return true if os[i] < os[j]
oi := os[i]
for x, v := range os[j] {
// lexicographic defintion: less if prefix or if element is <
if x == len(oi) || oi[x].Cmp(&v) < 0 {
return true
}
if oi[x].Cmp(&v) > 0 {
break
}
}
return false
})
// output sorted list
for _, o := range os {
fmt.Println(o)
}
}
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Haskell[edit]

Data.List[edit]

import Data.List ( sort , intercalate ) 
 
splitString :: Eq a => (a) -> [a] -> [[a]]
splitString c [] = []
splitString c s = let ( item , rest ) = break ( == c ) s
( _ , next ) = break ( /= c ) rest
in item : splitString c next
 
convertIntListToString :: [Int] -> String
convertIntListToString = intercalate "." . map show
 
orderOID :: [String] -> [String]
orderOID = map convertIntListToString . sort . map ( map read . splitString '.' )
 
oid :: [String]
oid = ["1.3.6.1.4.1.11.2.17.19.3.4.0.10" ,
"1.3.6.1.4.1.11.2.17.5.2.0.79" ,
"1.3.6.1.4.1.11.2.17.19.3.4.0.4" ,
"1.3.6.1.4.1.11150.3.4.0.1" ,
"1.3.6.1.4.1.11.2.17.19.3.4.0.1" ,
"1.3.6.1.4.1.11150.3.4.0"]
 
main :: IO ( )
main = do
mapM_ putStrLn $ orderOID oid
Output:
3.6.1.4.1.11.2.17.5.2.0.79
3.6.1.4.1.11.2.17.19.3.4.0.1
3.6.1.4.1.11.2.17.19.3.4.0.4
3.6.1.4.1.11.2.17.19.3.4.0.10
3.6.1.4.1.11150.3.4.0
3.6.1.4.1.11150.3.4.0.1

Data.Text[edit]

(To use split :: (Char -> Bool) -> Text -> [Text] in the standard libraries, we would have to temporarily convert the strings from [Char] to Text with pack and unpack)

import Data.Text (pack, split, unpack)
import Data.List (sort, intercalate)
 
-- SORTING OBJECT IDENTIFIERS ------------------------------------------------
oidSort :: [String] -> [String]
oidSort =
fmap (intercalate "." . fmap show) .
sort . fmap (fmap readInt . splitString '.')
 
-- GENERIC FUNCTIONS ---------------------------------------------------------
splitString :: Char -> String -> [String]
splitString c s = unpack <$> split (c ==) (pack s)
 
readInt :: String -> Int
readInt xs = read xs :: Int
 
-- TEST ----------------------------------------------------------------------
main :: IO ()
main =
mapM_ putStrLn $
oidSort
[ "1.3.6.1.4.1.11.2.17.19.3.4.0.10"
, "1.3.6.1.4.1.11.2.17.5.2.0.79"
, "1.3.6.1.4.1.11.2.17.19.3.4.0.4"
, "1.3.6.1.4.1.11150.3.4.0.1"
, "1.3.6.1.4.1.11.2.17.19.3.4.0.1"
, "1.3.6.1.4.1.11150.3.4.0"
]
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Where Data.List.Split is available (https://hackage.haskell.org/package/split-0.2.3.1/docs/Data-List-Split.html) we can alternatively write:

import Data.List.Split (splitOn)
import Data.List (sort, intercalate)
 
-- SORTING OBJECT IDENTIFIERS ------------------------------------------------
oidSort :: [String] -> [String]
oidSort =
fmap (intercalate "." . fmap show) . sort . fmap (fmap readInt . splitOn ".")
 
readInt :: String -> Int
readInt x = read x :: Int

jq[edit]

def data: [
"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0"
];
 
data | map( split(".") | map(tonumber) ) | sort | map(join("."))
Output:
[
  "1.3.6.1.4.1.11.2.17.5.2.0.79",
  "1.3.6.1.4.1.11.2.17.19.3.4.0.1",
  "1.3.6.1.4.1.11.2.17.19.3.4.0.4",
  "1.3.6.1.4.1.11.2.17.19.3.4.0.10",
  "1.3.6.1.4.1.11150.3.4.0",
  "1.3.6.1.4.1.11150.3.4.0.1"
]

J[edit]

Data:

oids=:<@-.&' ';._2]0 :0
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
)

In other words, for each line in that script, remove the spaces and put the rest in a box.

Sorting:

   >(/: __&".;._1&.('.'&,)&>) oids
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

In other words, for our sort key, we break the contents of each box by an initial '.' and treat the remainder as numbers.

We also pull the result out of its boxes for display purposes.

Kotlin[edit]

// version 1.0.6
 
class Oid(val id: String): Comparable<Oid> {
override fun compareTo(other: Oid): Int {
val splits1 = this.id.split('.')
val splits2 = other.id.split('.')
val minSize = if (splits1.size < splits2.size) splits1.size else splits2.size
for (i in 0 until minSize) {
if (splits1[i].toInt() < splits2[i].toInt()) return -1
else if (splits1[i].toInt() > splits2[i].toInt()) return 1
}
return splits1.size.compareTo(splits2.size)
}
 
override fun toString() = id
}
 
fun main(args: Array<String>) {
val oids = arrayOf(
Oid("1.3.6.1.4.1.11.2.17.19.3.4.0.10"),
Oid("1.3.6.1.4.1.11.2.17.5.2.0.79"),
Oid("1.3.6.1.4.1.11.2.17.19.3.4.0.4"),
Oid("1.3.6.1.4.1.11150.3.4.0.1"),
Oid("1.3.6.1.4.1.11.2.17.19.3.4.0.1"),
Oid("1.3.6.1.4.1.11150.3.4.0")
)
println(oids.sorted().joinToString("\n"))
}
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Lua[edit]

Using the in-built table.sort with a custom compare function.

local OIDs = {
"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0"
}
 
function compare (a, b)
local aList, bList, Na, Nb = {}, {}
for num in a:gmatch("%d+") do table.insert(aList, num) end
for num in b:gmatch("%d+") do table.insert(bList, num) end
for i = 1, math.max(#aList, #bList) do
Na, Nb = tonumber(aList[i]) or 0, tonumber(bList[i]) or 0
if Na ~= Nb then return Na < Nb end
end
end
 
table.sort(OIDs, compare)
for _, oid in pairs(OIDs) do print(oid) end
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Perl[edit]

my @OIDs = qw(
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
);
 
my @sorted =
map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [$_, join '', map { sprintf "%8d", $_ } split /\./, $_] }
@OIDs;
 
print "$_\n" for @sorted;
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Alternately, you can sort them as "version strings", which is a Perl syntax allowing you to specify a character string in the source code with the characters' codes specified as a dot-delimited sequence of integers.

my @sorted =
map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [$_, eval "v$_"] }
@OIDs;

Perl 6[edit]

The sort routine accepts a sort key callback as the first argument. Here we generate a list of integers as the sort key for each OID, which gets sorted lexicographically with numeric comparison by default.

.say for sort *.comb(/\d+/)».Int, <
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
>;
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Alternatively, using the sprintf-based approach used by the Perl solution, for comparison (input elided):

.say for sort *.split('.').fmt('%08d'), <...>;

Or if using a third-party module is acceptable:

use Sort::Naturally;
 
.say for sort &naturally, <...>;

Phix[edit]

I would normally recommend a tagsort, but we can avoid the extra routine and tagset here.

sequence strings = {"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0"}
 
constant len = length(strings)
sequence sortable = repeat(0,len)
 
for i=1 to len do
sequence si = split(strings[i],'.')
for j=1 to length(si) do
si[j] = to_number(si[j])
end for
sortable[i] = {si,i}
end for
sortable = sort(sortable)
for i=1 to len do
 ?strings[sortable[i][2]]
end for
"1.3.6.1.4.1.11.2.17.5.2.0.79"
"1.3.6.1.4.1.11.2.17.19.3.4.0.1"
"1.3.6.1.4.1.11.2.17.19.3.4.0.4"
"1.3.6.1.4.1.11.2.17.19.3.4.0.10"
"1.3.6.1.4.1.11150.3.4.0"
"1.3.6.1.4.1.11150.3.4.0.1"

PicoLisp[edit]

(for I
(by
'((L) (mapcar format (split (chop L) ".")))
sort
(quote
"1.3.6.1.4.1.11.2.17.19.3.4.0.10"
"1.3.6.1.4.1.11.2.17.5.2.0.79"
"1.3.6.1.4.1.11.2.17.19.3.4.0.4"
"1.3.6.1.4.1.11150.3.4.0.1"
"1.3.6.1.4.1.11.2.17.19.3.4.0.1"
"1.3.6.1.4.1.11150.3.4.0" ) )
(prinl I) )
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Python[edit]

We need to split the input and map each part to int otherwise elements gets compared as a string

 
data = [
'1.3.6.1.4.1.11.2.17.19.3.4.0.10',
'1.3.6.1.4.1.11.2.17.5.2.0.79',
'1.3.6.1.4.1.11.2.17.19.3.4.0.4',
'1.3.6.1.4.1.11150.3.4.0.1',
'1.3.6.1.4.1.11.2.17.19.3.4.0.1',
'1.3.6.1.4.1.11150.3.4.0'
]
 
for s in sorted(data, key=lambda x: list(map(int, x.split('.')))):
print(s)
 

Racket[edit]

#lang racket
(require data/order)
 
;; allows for key caching
(define (oid->oid-key o)
(map string->number (string-split o ".")))
 
(define oid-key< (order-<? datum-order))
 
(module+ test
(require rackunit)
(check-equal?
(sort
'("1.3.6.1.4.1.11.2.17.19.3.4.0.10"
"1.3.6.1.4.1.11.2.17.5.2.0.79"
"1.3.6.1.4.1.11.2.17.19.3.4.0.4"
"1.3.6.1.4.1.11150.3.4.0.1"
"1.3.6.1.4.1.11.2.17.19.3.4.0.1"
"1.3.6.1.4.1.11150.3.4.0")
oid-key<
#:key oid->oid-key
#:cache-keys? #t)
'("1.3.6.1.4.1.11.2.17.5.2.0.79"
"1.3.6.1.4.1.11.2.17.19.3.4.0.1"
"1.3.6.1.4.1.11.2.17.19.3.4.0.4"
"1.3.6.1.4.1.11.2.17.19.3.4.0.10"
"1.3.6.1.4.1.11150.3.4.0"
"1.3.6.1.4.1.11150.3.4.0.1")))

Tests run with no output, indicating success.

REXX[edit]

This REXX version supports negative integers in the OID.

/*REXX program performs a  sort  of  OID  (Object IDentifiers ◄── used in Network data).*/
$= 1.3.6.1.4.1.11.2.17.19.3.4.0.10 , /* ◄──┐ */
1.3.6.1.4.1.11.2.17.5.2.0.79 , /* ◄──┤ */
1.3.6.1.4.1.11.2.17.19.3.4.0.4 , /* ◄──┼─◄─ six OID numbers (as a list).*/
1.3.6.1.4.1.11150.3.4.0.1 , /* ◄──┤ */
1.3.6.1.4.1.11.2.17.19.3.4.0.1 , /* ◄──┤ */
1.3.6.1.4.1.11150.3.4.0 /* ◄──┘ */
call gen /*generate an array (@.) from the OIDs.*/
call show 'before sort ───► ' /*display the @ array before sorting.*/
say copies('░', 79) /*display fence, separate before &after*/
call adj 1; call bSort #; call adj 0 /*expand/sort/shrink the internal OID's*/
call show ' after sort ───► ' /*display the @ array after sorting. */
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
bSort: procedure expose @.; parse arg n; m=n-1 /*N: is the number of @ array elements.*/
do m=m for m by -1 until ok; ok=1 /*keep sorting the @ array until done. */
do j=1 for m; _=j+1; if @.j>@._ then parse value @.j @._ 0 with @._ @.j ok
end /*j*/ /* [↑] swap two out─of─order elements.*/
end /*m*/; return /* [↑] use a simple bubble sort. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
gen: #=words($); L=length(#); do i=1 for #; @.i=word($,i); end; return
/*length of the number of words in $.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
adj: arg LZ; do j=1 for #; x=translate(@.j, , .); y= /*construct X version. */
do k=1 for words(x); _=word(x, k) /*get a number in X. */
if LZ then y=y right(_,90,0); else y=y _+0 /*add│elide leading 0's*/
end /*k*/ /*adjust number, append*/
@.j = translate( space(y), ., ' ') /*reconstitute number. */
end /*j*/ /*LZ: Leading Zero(s). */
return /*── ─ ─ */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: do a=1 for #; say right("OID number",20) right(a,L) arg(1) @.a; end; return
output   when using the (internal) default input:
          OID number 1 before sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.10
          OID number 2 before sort ───►  1.3.6.1.4.1.11.2.17.5.2.0.79
          OID number 3 before sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.4
          OID number 4 before sort ───►  1.3.6.1.4.1.11150.3.4.0.1
          OID number 5 before sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.1
          OID number 6 before sort ───►  1.3.6.1.4.1.11150.3.4.0
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
          OID number 1  after sort ───►  1.3.6.1.4.1.11.2.17.5.2.0.79
          OID number 2  after sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.1
          OID number 3  after sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.4
          OID number 4  after sort ───►  1.3.6.1.4.1.11.2.17.19.3.4.0.10
          OID number 5  after sort ───►  1.3.6.1.4.1.11150.3.4.0
          OID number 6  after sort ───►  1.3.6.1.4.1.11150.3.4.0.1

Ring[edit]

This example needs updating due to a modification in the task. Please examine and fix the code if needed, then remove this message.
Details: the format description and test-case in the task description have been updated
 
 
/*
+--------------------------------------------------------------
+ Program Name : SortOIDNumeric.ring
+ Date  : 2016-07-14
+ Author  : Bert Mariani
+ Purpose  : Sort OID List in Numeric Order
+--------------------------------------------------------------
*/
 
oldOidList =
[
".1.3.6.1.4.1.11150.3.4.0.21",
".1.3.6.1.4.1.11150.3.4.0.11",
".1.3.6.1.4.1.11150.3.4.0.2",
".1.3.6.1.4.1.11150.3.4.0.1",
".1.3.6.1.4.1.11.2.17.3773.0.2",
".1.3.6.1.4.1.11.2.17.3773.0.1",
".1.3.6.1.4.1.11.2.17.19.3.4.0.32",
".1.3.6.1.4.1.11.2.17.19.3.4.0.31",
".1.3.6.1.4.1.11.2.17.19.3.4.0.25",
".1.3.6.1.4.1.11.2.17.19.3.4.0.22",
".1.3.6.1.4.1.11.2.17.19.3.4.0.19",
".1.3.6.1.4.1.11.2.17.19.3.4.0.10",
".1.3.6.1.4.1.11.2.17.19.3.4.0.4" ,
".1.3.6.1.4.1.11.2.17.19.3.4.0.3" ,
".1.3.6.1.4.1.11.2.17.19.3.4.0.2" ,
".1.3.6.1.4.1.11.2.17.19.3.4.0.1" ,
".1.3.6.1.4.1.11.2.17.19.2.0.79",
".1.3.6.1.4.1.11.2.17.19.2.0.9"
]
 
### SHOW BEFORE SORT
See nl + "oldOIDList Before Sort" +nl
See oldOidList
 
#---------------------
 
delChar = "."
nulChar = ""
padChar = " "
padSize = 6
newDotPadList = []
 
### Split list into lines
for line in oldOidList
 
### Split line by . into components
noDotList = str2list( substr(line, delChar, nl) )
 
### Pad components with left blanks to make equal size
newPadList = PadStringList(noDotList, padChar, padSize)
 
### Join the components back to a line
newDotPadString = JoinStringList(delChar, newPadList)
 
### Create new list - Alpha
Add(newDotPadList, newDotPadString)
next
 
### Sorts Alpha list
newDotPadListSorted = sort(newDotPadList)
 
### SHOW ALPHA INTERMEDIATE OUTPUT
See nl + "newDotPadListSorted Intermediate Sort" +nl
See newDotPadListSorted
 
### Remove blanks for original look
newOidList = RemovePadCharList( newDotPadListSorted, padChar, nulChar)
 
###--------------------
 
### SHOW AFTER SORT - NUMERIC
See nl + "newOIDList Final Sort" +nl
See newOidList
 
 
###--------------------------------------------------------------------
### Function: PadStringList
### newList = PadStringList(oldList, padChar, padSize )
###--------------------------------------------------------------------
 
Func PadStringList oldList, padChar, padSize
newList = []
for line in oldList
newPadSize = padSize - len(line)
newLine = Copy( padChar, newPadSize) + line
Add(newList, newLine)
next
 
### First line in all blank because of leading dot - remove
Del(newList,1)
return newList
 
###------------------------------------------------------------
### FUNC JoinStringList
### newString = JoinStringList( joinChar, oldList)
###------------------------------------------------------------
 
Func JoinStringList joinChar, OldList
newString = ""
for line in OldList
newString = newString + joinChar + line
next
return newString
 
###---------------------------------------------------------------------
### FUNC RemovePadCharList
### newOidList = RemovePadCharList( oldList, padChar, nulChar)
###---------------------------------------------------------------------
 
Func RemovePadCharList oldList, padChar, nulChar
newList = []
for line in oldList
noPadString = substr(line, padChar, nulChar)
Add(newList, noPadString)
next
return newList
###-----------------------------------------------------------
 
>;
Output:

.1.3.6.1.4.1.11.2.17.19.2.0.9
.1.3.6.1.4.1.11.2.17.19.2.0.79
.1.3.6.1.4.1.11.2.17.19.3.4.0.1
.1.3.6.1.4.1.11.2.17.19.3.4.0.2
.1.3.6.1.4.1.11.2.17.19.3.4.0.3
.1.3.6.1.4.1.11.2.17.19.3.4.0.4
.1.3.6.1.4.1.11.2.17.19.3.4.0.10
.1.3.6.1.4.1.11.2.17.19.3.4.0.19
.1.3.6.1.4.1.11.2.17.19.3.4.0.22
.1.3.6.1.4.1.11.2.17.19.3.4.0.25
.1.3.6.1.4.1.11.2.17.19.3.4.0.31
.1.3.6.1.4.1.11.2.17.19.3.4.0.32
.1.3.6.1.4.1.11.2.17.3773.0.1
.1.3.6.1.4.1.11.2.17.3773.0.2
.1.3.6.1.4.1.11150.3.4.0.1
.1.3.6.1.4.1.11150.3.4.0.2
.1.3.6.1.4.1.11150.3.4.0.11
.1.3.6.1.4.1.11150.3.4.0.21

Ruby[edit]

%w[
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
]
.sort_by{|oid| oid.split(".").map(&:to_i)}
.each{|oid| puts oid}
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Sidef[edit]

func sort_OIDs(ids) {
ids.sort_by { |id|
id.split('.').map { Num(_) }
}
}
 
var OIDs = %w(
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11150.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11150.3.4.0
)
 
sort_OIDs(OIDs).each { .say }
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1

Tcl[edit]

 
# Example input data:
set oid_list [list \
1.3.6.1.4.1.11.2.17.19.3.4.0.10 \
1.3.6.1.4.1.11.2.17.5.2.0.79 \
1.3.6.1.4.1.11.2.17.19.3.4.0.4 \
1.3.6.1.4.1.11150.3.4.0.1 \
1.3.6.1.4.1.11.2.17.19.3.4.0.1 \
1.3.6.1.4.1.11150.3.4.0 ]
set oid2_lists [list ]
set dots_max 0
set i 0
foreach oid $oid_list {
set oid_list [split $oid "."]
set dot_count [llength $oid_list]
incr dot_count -1
if { $dot_count > $dots_max } {
set dots_max $dot_count
}
set dots_arr(${i}) $dot_count
lappend oid2_lists $oid_list
incr i
}
# pad for strings of different dot counts
set oid3_lists [list]
for {set ii 0} {$ii < $i} {incr ii} {
set oid_list [lindex $oid2_lists $ii]
set add_fields [expr { $dots_max - $dots_arr(${ii}) } ]
if { $add_fields > 0 } {
for {set j 0} {$j < $add_fields} {incr j} {
lappend oid_list -1
}
}
lappend oid3_lists $oid_list
}
for {set n $dots_max} {$n >= 0 } {incr n -1} {
set oid3_lists [lsort -integer -index $n -increasing $oid3_lists]
}
# unpad strings of different dot counts
set oid4_lists [list]
for {set ii 0} {$ii < $i} {incr ii} {
set oid_list [lindex $oid3_lists $ii]
set j [lsearch -exact -integer $oid_list -1]
if { $j > -1 } {
set oid2_list [lrange $oid_list 0 ${j}-1]
lappend oid4_lists $oid2_list
} else {
lappend oid4_lists $oid_list
}
}
foreach oid_list $oid4_lists {
puts [join $oid_list "."]
}
 
Output:
% source sort-a-list-of-oids.tcl
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1


zkl[edit]

Translation of http://rosettacode.org/wiki/Natural_sorting#zkl

Basically, blow apart each line into a list of numbers and sort that.

fcn sortOIDS(oids){  // oids is not modified, a new list is created
// pad each oid with a terminal (-1) so zip won't short cut
oids=oids.pump(List(),fcn(oid){ (oid + ".-1").split(".").apply("toInt") });
oids.sort( // in place sort
fcn(a,b){ // a & b are (x,y,z,...-1), eg (0,4,2,54,-1), (4,6,-1)
a.zip(b).reduce(fcn(_,[(a,b)]){ // if one list longer, zip truncates
if(a==b) return(True); // continue to next field
return(Void.Stop,a<b); // OIDa<OIDb == cmp this field
},True);
});
oids.pump(List,fcn(list){ list[0,-1].concat(".") }) // back to strings
}
oids:=List(
"1.3.6.1.4.1.11.2.17.19.3.4.0.10",
"1.3.6.1.4.1.11.2.17.5.2.0.79",
"1.3.6.1.4.1.11.2.17.19.3.4.0.4",
"1.3.6.1.4.1.11150.3.4.0.1",
"1.3.6.1.4.1.11.2.17.19.3.4.0.1",
"1.3.6.1.4.1.11150.3.4.0");
oids=sortOIDS(oids);
oids.pump(Console.println); // print one OID per line
Output:
1.3.6.1.4.1.11.2.17.5.2.0.79
1.3.6.1.4.1.11.2.17.19.3.4.0.1
1.3.6.1.4.1.11.2.17.19.3.4.0.4
1.3.6.1.4.1.11.2.17.19.3.4.0.10
1.3.6.1.4.1.11150.3.4.0
1.3.6.1.4.1.11150.3.4.0.1