Sort a list of object identifiers

From Rosetta Code
Revision as of 23:45, 28 July 2021 by Laurence (talk | contribs) (Added Fōrmulæ solution)
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.

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

Related tasks

11l

Translation of: Python

<lang 11l>V 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’

]

V delim = ‘.’ // to get round ‘bug in MSVC 2017’[1]

L(s) sorted(data, key' x -> x.split(:delim).map(Int))

  print(s)</lang>
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

Ada

Works with: Ada version 2012

<lang Ada>with Ada.Containers.Generic_Array_Sort; with Ada.Strings.Fixed; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; with Ada.Unchecked_Deallocation;

procedure Sort_List_Identifiers is

  type Natural_Array is array (Positive range <>) of Natural;
  type Unbounded_String_Array is array(Positive range <>) of Unbounded_String;
  function To_Natural_Array(input : in String) return Natural_Array
  is
     target : Natural_Array(1 .. Ada.Strings.Fixed.Count(input, ".") + 1);
     from : Natural := input'First;
     to : Natural := Ada.Strings.Fixed.Index(input, ".");
     index : Positive := target'First;
  begin
     while to /= 0 loop
        target(index) := Natural'Value(input(from .. to - 1));
        from := to + 1;
        index := index + 1;
        to := Ada.Strings.Fixed.Index(input, ".", from);
     end loop;
     target(index) := Natural'Value(input(from .. input'Last));
     return target;
  end To_Natural_Array;
  function Lesser(Left, Right : in Unbounded_String) return Boolean is
  begin
     return To_Natural_Array(To_String(Left)) < To_Natural_Array(To_String(Right));
  end Lesser;
  procedure Sort is new Ada.Containers.Generic_Array_Sort
    (Index_Type   => Positive,
     Element_Type => Unbounded_String,
     Array_Type   => Unbounded_String_Array,
     "<"          => Lesser);
  table : Unbounded_String_Array :=
    (To_Unbounded_String("1.3.6.1.4.1.11.2.17.19.3.4.0.10"),
     To_Unbounded_String("1.3.6.1.4.1.11.2.17.5.2.0.79"),
     To_Unbounded_String("1.3.6.1.4.1.11.2.17.19.3.4.0.4"),
     To_Unbounded_String("1.3.6.1.4.1.11150.3.4.0.1"),
     To_Unbounded_String("1.3.6.1.4.1.11.2.17.19.3.4.0.1"),
     To_Unbounded_String("1.3.6.1.4.1.11150.3.4.0"));

begin

  Sort(table);
  for element of table loop
     Ada.Text_IO.Put_Line(To_String(element));
  end loop;

end Sort_List_Identifiers;</lang>

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

AppleScript

Vanilla

Place the call to the sort handler in a considering numeric strings statement. <lang applescript>(* Shell sort Algorithm: Donald Shell, 1959.

  • )

on ShellSort(theList, l, r)

   script o
       property lst : theList
   end script
   
   set listLength to (count theList)
   if (listLength > 1) then
       -- Convert negative and/or transposed range indices.
       if (l < 0) then set l to listLength + l + 1
       if (r < 0) then set r to listLength + r + 1
       if (l > r) then set {l, r} to {r, l}
       
       -- Do the sort.
       set stepSize to (r - l + 1) div 2
       repeat while (stepSize > 0)
           repeat with i from (l + stepSize) to r
               set currentValue to item i of o's lst
               repeat with j from (i - stepSize) to l by -stepSize
                   set thisValue to item j of o's lst
                   if (currentValue < thisValue) then
                       set item (j + stepSize) of o's lst to thisValue
                   else
                       set j to j + stepSize
                       exit repeat
                   end if
               end repeat
               if (j < i) then set item j of o's lst to currentValue
           end repeat
           set stepSize to (stepSize / 2.2) as integer
       end repeat
   end if
   
   return -- nothing. The input list has been sorted in place.

end ShellSort property sort : ShellSort

-- Test code: sort items 1 thru -1 (ie. all) of a list of strings, treating numeric portions numerically. set theList to {"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"}

considering numeric strings

   sort(theList, 1, -1)

end considering return theList</lang>

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"}

ASObjC

Use the localizedStandardCompare: string comparison method. <lang applescript>use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later use framework "Foundation"

set theList to {"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 theArray to current application's class "NSMutableArray"'s arrayWithArray:(theList) set theDescriptor to current application's class "NSSortDescriptor"'s sortDescriptorWithKey:("self") ¬

   ascending:(true) selector:("localizedStandardCompare:")

tell theArray to sortUsingDescriptors:({theDescriptor}) return theArray as list</lang>

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"}

Functional

As a composition of pure functions:

<lang applescript>------------- SORTED LIST OF OBJECT IDENTIFIERS ------------

-- sortedIdentifiers :: [String] -> [String] on sortedIdentifiers(xs)

   script listCompare
       on |λ|(x, y)
           script go
               on |λ|(a, xy)
                   if 0 ≠ a then
                       a
                   else
                       compare(|1| of xy, |2| of xy)
                   end if
               end |λ|
           end script
           foldl(go, 0, zip(x, y))
       end |λ|
   end script
   
   map(intercalate("."), ¬
       sortBy(listCompare, ¬
           map(compose(curry(my map)'s ¬
               |λ|(my readint), splitOn(".")), xs)))

end sortedIdentifiers



TEST --------------------------

on run

   unlines(sortedIdentifiers({¬
       "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"}))

end run



LIBRARY FUNCTIONS --------------------

-- Tuple (,) :: a -> b -> (a, b) on Tuple(a, b)

   -- Constructor for a pair of values, possibly of two different types.
   {type:"Tuple", |1|:a, |2|:b, length:2}

end Tuple


-- compare :: a -> a -> Ordering on compare(a, b)

   if a < b then
       -1
   else if a > b then
       1
   else
       0
   end if

end compare


-- compose (<<<) :: (b -> c) -> (a -> b) -> a -> c on compose(f, g)

   script
       property mf : mReturn(f)
       property mg : mReturn(g)
       on |λ|(x)
           mf's |λ|(mg's |λ|(x))
       end |λ|
   end script

end compose


-- concatMap :: (a -> [b]) -> [a] -> [b] on concatMap(f, xs)

   set lng to length of xs
   set acc to {}
   tell mReturn(f)
       repeat with i from 1 to lng
           set acc to acc & (|λ|(item i of xs, i, xs))
       end repeat
   end tell
   return acc

end concatMap


-- curry :: ((a, b) -> c) -> a -> b -> c on curry(f)

   script
       on |λ|(a)
           script
               on |λ|(b)
                   |λ|(a, b) of mReturn(f)
               end |λ|
           end script
       end |λ|
   end script

end curry


-- drop :: Int -> [a] -> [a] -- drop :: Int -> String -> String on drop(n, xs)

   set c to class of xs
   if script is not c then
       if string is not c then
           if n < length of xs then
               items (1 + n) thru -1 of xs
           else
               {}
           end if
       else
           if n < length of xs then
               text (1 + n) thru -1 of xs
           else
               ""
           end if
       end if
   else
       take(n, xs) -- consumed
       return xs
   end if

end drop


-- findIndices :: (a -> Bool) -> [a] -> [Int] on findIndices(p, xs)

   -- List of zero-based indices of 
   -- any matches for p in xs.
   script
       property f : mReturn(p)
       on |λ|(x, i, xs)
           if f's |λ|(x, i, xs) then
               {i - 1}
           else
               {}
           end if
       end |λ|
   end script
   concatMap(result, xs)

end findIndices


-- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs)

   tell mReturn(f)
       set v to startValue
       set lng to length of xs
       repeat with i from 1 to lng
           set v to |λ|(v, item i of xs, i, xs)
       end repeat
       return v
   end tell

end foldl


-- fst :: (a, b) -> a on fst(tpl)

   if class of tpl is record then
       |1| of tpl
   else
       item 1 of tpl
   end if

end fst


-- intercalate :: String -> [String] -> String on intercalate(delim)

   script
       on |λ|(xs)
           set {dlm, my text item delimiters} to ¬
               {my text item delimiters, delim}
           set s to xs as text
           set my text item delimiters to dlm
           s
       end |λ|
   end script

end intercalate


-- map :: (a -> b) -> [a] -> [b] on map(f, xs)

   -- The list obtained by applying f
   -- to each element of xs.
   tell mReturn(f)
       set lng to length of xs
       set lst to {}
       repeat with i from 1 to lng
           set end of lst to |λ|(item i of xs, i, xs)
       end repeat
       return lst
   end tell

end map


-- mReturn :: First-class m => (a -> b) -> m (a -> b) on mReturn(f)

   -- 2nd class handler function lifted into 1st class script wrapper. 
   if script is class of f then
       f
   else
       script
           property |λ| : f
       end script
   end if

end mReturn


-- Returns a sequence-matching function for findIndices etc -- matching :: [a] -> (a -> Int -> [a] -> Bool) -- matching :: String -> (Char -> Int -> String -> Bool) on matching(pat)

   if class of pat is text then
       set xs to characters of pat
   else
       set xs to pat
   end if
   set lng to length of xs
   set bln to 0 < lng
   if bln then
       set h to item 1 of xs
   else
       set h to missing value
   end if
   script
       on |λ|(x, i, src)
           (h = x) and xs = ¬
               (items i thru min(length of src, -1 + lng + i) of src)
       end |λ|
   end script

end matching


-- min :: Ord a => a -> a -> a on min(x, y)

   if y < x then
       y
   else
       x
   end if

end min


-- partition :: (a -> Bool) -> [a] -> ([a], [a]) on partition(f, xs)

   tell mReturn(f)
       set ys to {}
       set zs to {}
       repeat with x in xs
           set v to contents of x
           if |λ|(v) then
               set end of ys to v
           else
               set end of zs to v
           end if
       end repeat
   end tell
   Tuple(ys, zs)

end partition


-- readInt :: String -> Int on readint(s)

   s as integer

end readint


-- snd :: (a, b) -> b on snd(tpl)

   if class of tpl is record then
       |2| of tpl
   else
       item 2 of tpl
   end if

end snd


-- Enough for small scale sorts. -- Use instead sortOn (Ord b => (a -> b) -> [a] -> [a]) -- which is equivalent to the more flexible sortBy(comparing(f), xs) -- and uses a much faster ObjC NSArray sort method -- sortBy :: (a -> a -> Ordering) -> [a] -> [a] on sortBy(f, xs)

   if length of xs > 1 then
       set h to item 1 of xs
       set f to mReturn(f)
       script
           on |λ|(x)
               f's |λ|(x, h) ≤ 0
           end |λ|
       end script
       set lessMore to partition(result, rest of xs)
       sortBy(f, |1| of lessMore) & {h} & ¬
           sortBy(f, |2| of lessMore)
   else
       xs
   end if

end sortBy


-- splitOn :: [a] -> [a] -> a -- splitOn :: String -> String -> [String] on splitOn(pat)

   script
       on |λ|(src)
           if class of src is text then
               set {dlm, my text item delimiters} to ¬
                   {my text item delimiters, pat}
               set xs to text items of src
               set my text item delimiters to dlm
               return xs
           else
               set lng to length of pat
               script residue
                   on |λ|(a, i)
                       Tuple(fst(a) & ¬
                           {init(items snd(a) thru (i) of src)}, lng + i)
                   end |λ|
               end script
               set tpl to foldl(residue, ¬
                   Tuple({}, 1), findIndices(matching(pat), src))
               return fst(tpl) & {drop(snd(tpl) - 1, src)}
           end if
       end |λ|
   end script

end splitOn


-- unlines :: [String] -> String on unlines(xs)

   -- A single string formed by the intercalation
   -- of a list of strings with the newline character.
   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, linefeed}
   set s to xs as text
   set my text item delimiters to dlm
   s

end unlines


-- zip :: [a] -> [b] -> [(a, b)] on zip(xs, ys)

   zipWith(my Tuple, xs, ys)

end zip


-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] on zipWith(f, xs, ys)

   set lng to min(length of xs, length of ys)
   set lst to {}
   if 1 > lng then
       return {}
   else
       tell mReturn(f)
           repeat with i from 1 to lng
               set end of lst to |λ|(item i of xs, item i of ys)
           end repeat
           return lst
       end tell
   end if

end zipWith</lang>

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

AutoHotkey

<lang AutoHotkey>; based on http://www.rosettacode.org/wiki/Sorting_algorithms/Quicksort#AutoHotkey OidQuickSort(a, Delim:=".", index:=1){

   if (a.Count() <= 1)
       return a
   Less := [], Equal := [], More := []
   Pivot := StrSplit(a[1], Delim)[index]
   for k, v in a
   {
       x := StrSplit(v, Delim)[index]
       if (x < Pivot)
           less.InsertAt(1, v)
       else if (x > Pivot)
           more.InsertAt(1, v)
       else
           Equal.InsertAt(1, v)
   }    
   Equal := OidQuickSort(Equal, Delim, index+1)
   Less  := OidQuickSort(Less)
   Out	  := OidQuickSort(More)
   if (Equal.Count())
       Out.InsertAt(1, Equal*)	; InsertAt all values of Equal at index 1
   if (Less.Count())
       Out.InsertAt(1, Less*)	; InsertAt all values of Less at index 1
   return Out

}</lang> Examples:<lang AutoHotkey>a := ["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 k, v in OidQuickSort(a)

   Out .= "`n" v

MsgBox % Out return</lang>

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

AWK

<lang AWK>

  1. syntax: GAWK -f SORT_A_LIST_OF_OBJECT_IDENTIFIERS.AWK
  2. sorting:
  3. PROCINFO["sorted_in"] is used by GAWK
  4. SORTTYPE is used by Thompson Automation's TAWK

BEGIN {

   width = 10
   oid_arr[++n] = "1.3.6.1.4.1.11.2.17.19.3.4.0.10"
   oid_arr[++n] = "1.3.6.1.4.1.11.2.17.5.2.0.79"
   oid_arr[++n] = "1.3.6.1.4.1.11.2.17.19.3.4.0.4"
   oid_arr[++n] = "1.3.6.1.4.1.11150.3.4.0.1"
   oid_arr[++n] = "1.3.6.1.4.1.11.2.17.19.3.4.0.1"
   oid_arr[++n] = "1.3.6.1.4.1.11150.3.4.0"
  1. oid_arr[++n] = "1.11111111111.1" # un-comment to test error
   for (i=1; i<=n; i++) {
     str = ""
     for (j=1; j<=split(oid_arr[i],arr2,"."); j++) {
       str = sprintf("%s%*s.",str,width,arr2[j])
       if ((leng = length(arr2[j])) > width) {
         printf("error: increase sort key width from %d to %d for entry %s\n",width,leng,oid_arr[i])
         exit(1)
       }
     }
     arr3[str] = ""
   }
   PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
   for (i in arr3) {
     str = i
     gsub(/ /,"",str)
     sub(/\.$/,"",str)
     printf("%s\n",str)
   }
   exit(0)

} </lang>

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

A C99 (or later) compiler is required. <lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>

typedef struct oid_tag {

   char* str_;
   int* numbers_;
   int length_;

} oid;

// free memory, no-op if p is null void oid_destroy(oid* p) {

   if (p != 0) {
       free(p->str_);
       free(p->numbers_);
       free(p);
   }

}

int char_count(const char* str, char ch) {

   int count = 0;
   for (const char* p = str; *p; ++p) {
       if (*p == ch)
           ++count;
   }
   return count;

}

// construct an OID from a string // returns 0 on memory allocation failure or parse error oid* oid_create(const char* str) {

   oid* ptr = calloc(1, sizeof(oid));
   if (ptr == 0)
       return 0;
   ptr->str_ = strdup(str);
   if (ptr->str_ == 0) {
       oid_destroy(ptr);
       return 0;
   }
   int dots = char_count(str, '.');
   ptr->numbers_ = malloc(sizeof(int) * (dots + 1));
   if (ptr->numbers_ == 0) {
       oid_destroy(ptr);
       return 0;
   }
   ptr->length_ = dots + 1;
   const char* p = str;
   for (int i = 0; i <= dots && *p;) {
       char* eptr = 0;
       int num = strtol(p, &eptr, 10);
       if (*eptr != 0 && *eptr != '.') {
           // TODO: check for overflow/underflow
           oid_destroy(ptr);
           return 0;
       }
       ptr->numbers_[i++] = num;
       p = eptr;
       if (*p)
           ++p;
   }
   return ptr;

}

// compare two OIDs int oid_compare(const void* p1, const void* p2) {

   const oid* o1 = *(oid* const*)p1;
   const oid* o2 = *(oid* const*)p2;
   int i1 = 0, i2 = 0;
   for (; i1 < o1->length_ && i2 < o2->length_; ++i1, ++i2) {
       if (o1->numbers_[i1] < o2->numbers_[i2])
           return -1;
       if (o1->numbers_[i1] > o2->numbers_[i2])
           return 1;
   }
   if (o1->length_ < o2->length_)
       return -1;
   if (o1->length_ > o2->length_)
       return 1;
   return 0;

}

int main() {

   const char* input[] = {
       "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"
   };
   const int len = sizeof(input)/sizeof(input[0]);
   oid* oids[len];
   memset(oids, 0, sizeof(oids));
   int i;
   for (i = 0; i < len; ++i) {
       oids[i] = oid_create(input[i]);
       if (oids[i] == 0)
       {
           fprintf(stderr, "Out of memory\n");
           goto cleanup;
       }
   }
   qsort(oids, len, sizeof(oid*), oid_compare);
   for (i = 0; i < len; ++i)
       puts(oids[i]->str_);

cleanup:

   for (i = 0; i < len; ++i)
       oid_destroy(oids[i]);
   return 0;

}</lang>

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#

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

}</lang>

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++

<lang Cpp>#include <string>

  1. include <vector>
  2. include <algorithm>
  3. include <boost/tokenizer.hpp>
  4. 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 ;

}</lang>

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


Clojure

Clojure 'sort' function allows specifying an optional comparator function. In this case, our custom comparator utilizes the ability of the clojure.core 'compare' function to compare vectors in an appropriate fashion.

<lang Clojure> (defn oid-vec [oid-str]

 (->> (clojure.string/split oid-str #"\.")
      (map #(Long. %))))

(defn oid-str [oid-vec]

 (clojure.string/join "." oid-vec))
If vals differ before shorter vec ends,
use comparison of that "common header".
If common part matches, compare based on num elements.

(defn oid-compare [a b]

 (let [min-len    (min (count a) (count b))
       common-cmp (compare (vec (take min-len a))
                           (vec (take min-len b)))]
   (if (zero? common-cmp)
     (compare (count a) (count b))
     common-cmp)))

(defn sort-oids [oid-strs]

 (->> (map  oid-vec oid-strs)
      (sort oid-compare)
      (map  oid-str)))

</lang>

Output:
  (sort-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"])

  ("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

<lang lisp>(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))))</lang>
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

<lang elixir>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)</lang>

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

Factor

Factor provides the human<=> word which converts numbers in a string to integers before comparing them. <lang factor>USING: io qw sequences sorting sorting.human ;

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 

} [ human<=> ] sort [ print ] each</lang>

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

Fōrmulæ

Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.

Programs in Fōrmulæ are created/edited online in its website, However they run on execution servers. By default remote servers are used, but they are limited in memory and processing power, since they are intended for demonstration and casual use. A local server can be downloaded and installed, it has no limitations (it runs in your own computer). Because of that, example programs can be fully visualized and edited, but some of them will not run if they require a moderate or heavy computation/memory resources, and no local server is being used.

In this page you can see the program(s) related to this task and their results.

Go

<lang go>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)
   }

}</lang>

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

Data.List

<lang Haskell>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</lang>
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

(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)

<lang haskell>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"
   ]</lang>
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:

<lang haskell>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</lang>

J

Data:

<lang J>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

)</lang>

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

Sorting:

<lang J> >(/: __&".;._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 </lang>

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.

Java

Works with: Java version 8 or higher

<lang java> package com.rosettacode;

import java.util.Comparator; import java.util.stream.Stream;

public class OIDListSorting {

   public static void main(String[] args) {
       final String dot = "\\.";
       final Comparator<String> oids_comparator = (o1, o2) -> {
           final String[] o1Numbers = o1.split(dot), o2Numbers = o2.split(dot);
           for (int i = 0; ; i++) {
               if (i == o1Numbers.length && i == o2Numbers.length)
                   return 0;
               if (i == o1Numbers.length)
                   return -1;
               if (i == o2Numbers.length)
                   return 1;
               final int nextO1Number = Integer.valueOf(o1Numbers[i]), nextO2Number = Integer.valueOf(o2Numbers[i]);
               final int result = Integer.compare(nextO1Number, nextO2Number);
               if (result != 0)
                   return result;
           }
       };
       Stream.of("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")
               .sorted(oids_comparator)
               .forEach(System.out::println);
   }

}</lang>

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

jq

<lang jq>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("."))</lang>

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"
]

Julia

Works with: Julia version 0.6

<lang julia>oidlist = ["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!(oidlist; lt=lexless,

   by=x -> parse.(Int, String.(split(x, "."))))

println.(oidlist)</lang>

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

Kotlin

<lang scala>// 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"))

}</lang>

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

Using the in-built table.sort with a custom compare function. <lang Lua>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</lang>

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

Using Coroutine

<lang lua> local function oidGen(s)

 local wrap, yield = coroutine.wrap, coroutine.yield
 return wrap(function()
   for n in s:gmatch"%d+"do yield(tonumber(n))end
 end)

end

local function oidCmp(a,b)

 local agen,bgen = oidGen(a),oidGen(b)
 local n,m = agen(),bgen()
 while n and m do
   if n~=m then return n<m end
   n,m = agen(),bgen()
 end
 return m and true or false -- bgen longer with previous equal

end

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"

}

table.sort(OIDs, oidCmp) for _, oid in pairs(OIDs) do print(oid) end </lang>

M2000 Interpreter

In this example we have to change dot to #, to make each number as an integer one. <lang M2000 Interpreter> Module CheckIt {

     Flush ' empty stack of values
     Data "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.1",  "1.3.6.1.4.1.11150.3.4.0.1"
     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.11150.3.4.0"
     \\ Inventories of type queue can get same keys, and have sort where numbers (float type) as part of key count as numbers
     Inventory queue OID
     \\ prepare keys (replace dot to #)
     While not empty {
           Append OID, Replace$(".","#", letter$)
     }
     Sort Ascending OID
     n=Each(OID)
     a$=""
     While n {
           \\ replace # to dot
           a$+=Replace$("#",".", Eval$(n))+{
           }
     }
     Clipboard a$
     Report a$

} Checkit </lang>

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

Using Piece$() and Stack Sort

We use a stack (a linked list) to save numbers, and a function to check piece by piece for "Grater than" only

piece$(a$,".")(i) works from 0

piece$(a$,".", i) works from 1

piece$(a$,".") export a pointer to an array with each piece on it


<lang M2000 Interpreter> GT=lambda (a$, b$)->{

     def i
     do {
           m$=piece$(a$,".")(i)
           n$=piece$(b$,".")(i)
           i++
     } until n$="" or m$="" or m$<>n$
     if n$="" then =m$<>"":exit
     if m$="" then =False:exit
     =val(m$)>val(n$)     

} Stack new {

     \\ data push to end of stack (we use it as FIFO)
     data "1.3.6.1.4.1.11.2.17.19.3.4.0.10"
     data "1.3.6.1.4.1.11.2.17.5.2.0.79"
     data "1.3.6.1.4.1.11.2.17.19.3.4.0.4"
     data "1.3.6.1.4.1.11150.3.4.0.1"
     data "1.3.6.1.4.1.11.2.17.19.3.4.0.1"
     data "1.3.6.1.4.1.11150.3.4.0"
     M=Stack.Size-1
     While M>0 {
                 N=1
                 For i=1 to M {
                       \\ if peek item i > peek item i+1 then get i+1 to top, and send to i
                       \\ stack is a linked list, so moving items done with pointers only
                       if Gt(stackitem$(i), stackitem$(i+1)) then Shift i+1 : ShiftBack i : N=i
                 }
                 M=N-1
     }
     While not empty {
           Print Letter$
     }

} </lang>

Using a function which split pieces one time. We have to insert one more item, by append a "." to a$ and b$ <lang M2000 Interpreter>

     GT=lambda (a$, b$)->{
           def i=-1
           dim Base 0,  a$(), b$()
           a$()=piece$(a$+".", ".")
           b$()=piece$(b$+".", ".")
           do {
                 i++
           } until a$(i)="" or b$(i)="" or a$(i)<>b$(i)
           if  b$(i)="" then =a$(i)<>"":exit
           if a$(i)="" then =False:exit
           =val(a$(i))>val(b$(i))     
     }

</lang>

Using QuickSort

We can make any OID as array of numbers and we can put all OIDs in an array to sort by a custom Quick Sort, where we place the compare function as a lambda function. Swaps made to pointers of arrays of OIDs. There is no need to use PIECE$() in compare function.

Note that QuickSort need Lower or Equal

There is a Let numeric_expression = string_expression Normally this numeric_expression = string_expression is a syntax error, but Let is a two part statement, a Push to stack and a Read from stack: Push string_expression : Read numeric_expression So first executed the string expression which return a pointer to array and then the read statement get this pointer;


<lang M2000 Interpreter> Group Quick { Private:

     Function partition {
              Read &A(), p, r
              x = A(r)
              i = p-1
              For j=p to r-1 {
                  If .LE(A(j), x) Then {
                         i++
                         Swap A(i),A(j)
                      }
               }
               Swap A(i+1),A(r)
              = i+1
           }

Public:

     LE=Lambda->False
     Function quicksort {
          Read &A(), p, r
          If p < r Then {
            q = .partition(&A(), p, r)
            Call .quicksort(&A(), p, q - 1)
            Call .quicksort(&A(), q + 1, r)
         }
     }

} \\ no easy way to join ; \\ n^ is the cursor from iterator n Function join$(a$()) {

     n=each(a$(), 1, -2)
     k$=""
     while n {
           overwrite k$, ".", n^:=array$(n)
     }
     =k$

} Stack New {

           Data "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.1",  "1.3.6.1.4.1.11150.3.4.0.1"
           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.11150.3.4.0"
           Dim  Base 0, arr(Stack.Size)
           link arr() to arr$()
           i=0 :  While not Empty {arr$(i)=piece$(letter$+".", ".") : i++ }

} Print "Unsorted" For i=0 to len(arr())-1 {

     Print join$(arr(i))   

} Quick.LE=lambda (a, b)->{

     Link a, b to a$(), b$()
      def i=-1
      do {
            i++
      } until a$(i)="" or b$(i)="" or a$(i)<>b$(i)
      if  b$(i)="" then =a$(i)="":exit
      if a$(i)="" then =true:exit
      =val(a$(i))<=val(b$(i))     

} Call Quick.quicksort(&arr(), 0, Len(arr())-1) Print "Sorted" For i=0 to len(arr())-1 {

     Print join$(arr(i))   

} </lang>

Mathematica / Wolfram Language

<lang Mathematica>in = {"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 = StringSplit[#, "."] & /@ in; in = Map[ToExpression, in, {2}]; Column[StringRiffle[ToString /@ #, "."] & /@ LexicographicSort[in]]</lang>

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

Nim

OID as distinct string

Nim allows to define distinct types. As OID are peculiar strings, defining them as distinct strings seems a good idea. We have to define a specific comparison procedure and that’s all. Here is the code: <lang Nim>import algorithm, sequtils, strutils

type OID = distinct string

  1. Borrow the `$` procedure from the base string type.

proc `$`(oid: OID): string {.borrow.}


template toSeqInt(oid: OID): seq[int] =

 ## Convert an OID into a sequence of integers.
 oid.string.split('.').map(parseInt)


proc oidCmp(a, b: OID): int =

 ## Compare two OIDs. Return 0 if OIDs are equal, -1 if the first is
 ## less than the second, +1 is the first is greater than the second.
 let aseq = a.toSeqInt
 let bseq = b.toSeqInt
 for i in 0..<min(aseq.len, bseq.len):
   result = cmp(aseq[i], bseq[i])
   if result != 0: return
 result = cmp(aseq.len, bseq.len)


when isMainModule:

 const OIDS = [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"]
 for oid in OIDS.sorted(oidCmp):
   echo oid</lang>

Note that as the type is distinct, we have to borrow the procedure `$` to the string type in order to print OID values.

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

OID as composite object

The previous method is elegant but not very efficient. Indeed, every time a comparison is done, two conversions to a sequence of integers are done. And when sorting an array of OIDs, the same conversion is done several time.

To avoid this, we can define OID as a composite object type containing a string value and a list of integers to use for the comparisons. The code is not really more complicated, only arguably less elegant as we have to create the OIDs using the procedure “initOID”.

<lang Nim>import algorithm, sequtils, strutils

type OID = object

 value: string
 list: seq[int]

proc initOID(s: string): OID =

 OID(value: s, list: s.split('.').map(parseInt))

proc `$`(oid: OID): string =

 oid.value

proc oidCmp(a, b: OID): int =

 ## Compare two OIDs. Return 0 if OIDs are equal, -1 if the first is
 ## less than the second, +1 is the first is greater than the second.
 for i in 0..<min(a.list.len, b.list.len):
   result = cmp(a.list[i], b.list[i])
   if result != 0: return
 result = cmp(a.list.len, b.list.len)


when isMainModule:

 const OIDS = [initOID("1.3.6.1.4.1.11.2.17.19.3.4.0.10"),
               initOID("1.3.6.1.4.1.11.2.17.5.2.0.79"),
               initOID("1.3.6.1.4.1.11.2.17.19.3.4.0.4"),
               initOID("1.3.6.1.4.1.11150.3.4.0.1"),
               initOID("1.3.6.1.4.1.11.2.17.19.3.4.0.1"),
               initOID("1.3.6.1.4.1.11150.3.4.0")]
 for oid in OIDS.sorted(oidCmp):
   echo oid</lang>
Output:

Same as with the previous method.

Perl

<lang perl>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;</lang>

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.

<lang perl>my @sorted =

   map { $_->[0] }
   sort { $a->[1] cmp $b->[1] }
   map { [$_, eval "v$_"] }
   @OIDs;</lang>

Phix

Library: Phix/basics

This is a variation on a standard tagsort, but performed a bit more explicitly.

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
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"

alternative

This is very similar to the above, but without using any tags/indexes at all.

constant 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"}

function each(string original)
    sequence sortable = apply(split(original,'.'),to_number)
    return {sortable,original}
end function
-- sort on sortable, then use vslice to extract the originals:
printf(1,"%s\n",join(vslice(sort(apply(strings,each)),2),"\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

Phixmonti

<lang Phixmonti>include ..\Utilitys.pmt

( "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" )

len for

   var i
   i get "." " " subst split
   len for
       var j
       j get tonum j set
   endfor
   i set

endfor

sort

len for

   var i
   i get
   "" var string
   len for
       get tostr "." chain string swap chain var string
   endfor
   drop
   string 0 del i set

endfor

len for get print nl endfor </lang>

PicoLisp

<lang PicoLisp>(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) )</lang>
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

Prolog

Works with: SWI Prolog

<lang prolog>main:-

   sort_oid_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"], Sorted_list),
   foreach(member(oid(_, Oid), Sorted_list), writeln(Oid)).

sort_oid_list(Oid_list, Sorted_list):-

   parse_oid_list(Oid_list, Parsed),
   sort(1, @=<, Parsed, Sorted_list).

parse_oid_list([], []):-!. parse_oid_list([Oid|Oid_list], [oid(Numbers, Oid)|Parsed]):-

   parse_oid(Oid, Numbers),
   parse_oid_list(Oid_list, Parsed).

parse_oid(Oid, Numbers):-

   split_string(Oid, ".", ".", Strings),
   number_strings(Numbers, Strings).

number_strings([], []):-!. number_strings([Number|Numbers], [String|Strings]):-

   number_string(Number, String),
   number_strings(Numbers, Strings).</lang>
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

We need to split the input and map each part to int otherwise elements gets compared as a string <lang Python> 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)

</lang>

Racket

<lang racket>#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")))</lang>

Tests run with no output, indicating success.

Raku

(formerly Perl 6)

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.

<lang perl6>.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

>;</lang>

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):

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

Or if using a third-party module is acceptable:

<lang perl6>use Sort::Naturally;

.say for sort &naturally, <...>;</lang>

REXX

This REXX version supports negative integers in the OID. <lang rexx>/*REXX program performs a sort of OID (Object IDentifiers ◄── used in Network data).*/ call gen /*generate an array (@.) from the OIDs.*/ call show 'before sort ───► ' /*display the @ array before sorting.*/

                            say copies('░', 79) /*   "   fence, separate before & after*/

call adj 1 /*expand the $ list of OID numbers. */ call bSort # /*sort " " " " " " */ call adj 0 /*shrink " " " " " " */ 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         /*calculate the next (index) in @ array*/
            if @.j>@._  then parse value    @.j  @._  0      with      @._  @.j  ok
            end   /*j*/                         /* [↑]  swap two out─of─order elements.*/
         end      /*m*/                         /* [↑]  use a simple  bubble  sort.    */
      return

/*──────────────────────────────────────────────────────────────────────────────────────*/ gen: $= 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             /* ◄──┘                                */
      w= 0                                                      /*W: max length of #'s.*/
      #= words($);     do i=1  for #;    @.i= word($, i);    w= max(w, length(@.i) )
                       end   /*i*/                              /*W: max length of #'s.*/
      return

/*──────────────────────────────────────────────────────────────────────────────────────*/ 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(_, w, 0)               /*(prepend) leading 0's*/
                         else y= y    (_   +   0)               /* (elide)     "     " */
                  end   /*k*/
               @.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,length(#)) arg(1) @.a; end; return</lang>

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

<lang Ring>

/*

+--------------------------------------------------------------
+        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.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" ]

       ### 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


      1. --------------------------------------------------------------------
      2. Function: PadStringList
      3. newList = PadStringList(oldList, padChar, padSize )
      4. --------------------------------------------------------------------

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

      1. ------------------------------------------------------------
      2. FUNC JoinStringList
      3. newString = JoinStringList( joinChar, oldList)
      4. ------------------------------------------------------------

Func JoinStringList joinChar, OldList

   newString = ""
   for line in OldList
       newString = newString + joinChar + line
   next

return newString

      1. ---------------------------------------------------------------------
      2. FUNC RemovePadCharList
      3. newOidList = RemovePadCharList( oldList, padChar, nulChar)
      4. ---------------------------------------------------------------------

Func RemovePadCharList oldList, padChar, nulChar

   newList = []
   for line in oldList
         noPadString = substr(line, padChar, nulChar)
       Add(newList, noPadString)
   next

return newList

      1. -----------------------------------------------------------

>;</lang>

Output:

newOIDList Final Sort
.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

Ruby

<lang ruby>%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}</lang>

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

Or, using the Gem module (which knows about versions): <lang ruby>puts %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| Gem::Version.new(oid) }</lang> with identical output.

Rust

<lang Rust>fn split(s: &str) -> impl Iterator<Item = u64> + '_ {

   s.split('.').map(|x| x.parse().unwrap())

}

fn main() {

   let mut oids = vec![
       "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.sort_by(|a, b| Iterator::cmp(split(a), split(b)));
   
   println!("{:#?}", oids);

}</lang>

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"
]

Sather

<lang sather>class MAIN is

  oid_lt (a, b: STR): BOOL is
     as ::= a.cursor.split('.');
     bs ::= b.cursor.split('.');
     loop
        na ::= #INT(as.elt!);
        nb ::= #INT(bs.elt!);
        if na /= nb then return na < nb; end;
     end;
     return as.size < bs.size;
  end;
  main is
     sorter: ARR_SORT_ALG{STR, ARRAY{STR}};
     input: ARRAY{STR} := |"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"|;
     sorted ::= input.copy;
     sorter.sort_by(sorted, bind(oid_lt(_, _)));
     #OUT+"unsorted:\n";
     loop #OUT+input.elt! + "\n"; end;
     #OUT+"sorted:\n";
     loop #OUT+sorted.elt! + "\n"; end;
  end;

end;</lang>

Output:
unsorted:
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
sorted:
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

<lang ruby>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 }</lang>

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

Swift

<lang swift>import Foundation

public struct OID {

 public var val: String
 public init(_ val: String) {
   self.val = val
 }

}

extension OID: CustomStringConvertible {

 public var description: String {
   return val
 }

}

extension OID: Comparable {

 public static func < (lhs: OID, rhs: OID) -> Bool {
   let split1 = lhs.val.components(separatedBy: ".").compactMap(Int.init)
   let split2 = rhs.val.components(separatedBy: ".").compactMap(Int.init)
   let minSize = min(split1.count, split2.count)
   for i in 0..<minSize {
     if split1[i] < split2[i] {
       return true
     } else if split1[i] > split2[i] {
       return false
     }
   }
   return split1.count < split2.count
 }
 public static func == (lhs: OID, rhs: OID) -> Bool {
   return lhs.val == rhs.val
 }

}

let ids = [

 "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"

].map(OID.init)

for id in ids.sorted() {

 print(id)

}</lang>

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

<lang Tcl>

  1. 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

}

  1. 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]

}

  1. 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 "."]

} </lang>

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

VBScript

<lang vb>' Sort a list of object identifiers - VBScript function myCompare(x,y) dim i,b sx=split(x,".") sy=split(y,".") b=false for i=0 to ubound(sx) if i > ubound(sy) then b=true: exit for select case sgn(int(sx(i))-int(sy(i))) case 1: b=true: exit for case -1: b=false: exit for end select next myCompare=b end function

function bubbleSort(t) dim i,n n=ubound(t) do changed=false n= n-1 for i=0 to n if myCompare(t(i),t(i+1)) then tmp=t(i): t(i)=t(i+1): t(i+1)=tmp changed=true end if next loop until not changed bubbleSort=t end function

a=array( _ "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") bubbleSort a wscript.echo join(a,vbCrlf) </lang>

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

Wren

Library: Wren-fmt
Library: Wren-sort

<lang ecmascript>import "/fmt" for Fmt import "/sort" for Sort

var 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"

]

oids = oids.map { |oid| Fmt.v("s", 5, oid.split("."), 0, ".", "") }.toList Sort.quick(oids) oids = oids.map { |oid| oid.replace(" ", "") }.toList System.print(oids.join("\n"))</lang>

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

zkl

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

Basically, blow apart each line into a list of numbers and sort that. <lang zkl>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

}</lang> <lang zkl>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</lang>

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