Sort a list of object identifiers

From Rosetta Code
(Redirected from Sort by OID numbers)
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[edit]

Translation of: Python
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’[https://developercommunity.visualstudio.com/t/bug-with-operator-in-c/565417]

L(s) sorted(data, key' x -> x.split(:delim).map(Int))
   print(s)
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

Action![edit]

DEFINE PTR="CARD"

PROC PrintArray(PTR ARRAY a INT size)
  INT i

  FOR i=0 TO size-1
  DO
    PrintE(a(i))
  OD
RETURN

INT FUNC Decode(CHAR ARRAY s INT ARRAY a)
  INT count
  BYTE i,begin,end
  CHAR ARRAY tmp(10)

  count=0 i=1
  WHILE i<=s(0)
  DO
    begin=i
    WHILE i<=s(0) AND s(i)#'.
    DO i==+1 OD
    end=i-1
    IF i<s(0) THEN i==+1 FI
    SCopyS(tmp,s,begin,end)
    a(count)=ValI(tmp)
    count==+1
  OD
RETURN (count)

INT FUNC Compare(CHAR ARRAY s1,s2)
  INT ARRAY a1(20),a2(20)
  INT c1,c2,res
  BYTE i

  c1=Decode(s1,a1)
  c2=Decode(s2,a2)

  i=0
  DO
    IF i=c1 AND i=c2 THEN
      RETURN (0)
    ELSEIF i=c1 THEN
      RETURN (-1)
    ELSEIF i=c2 THEN
      RETURN (1)
    FI

    IF a1(i)<a2(i) THEN
      RETURN (-1)
    ELSEIF a1(i)>a2(i) THEN
      RETURN (1)
    FI
    i==+1
  OD
RETURN (0)

PROC Sort(PTR ARRAY a INT size)
  INT i,j,minpos
  CHAR ARRAY tmp

  FOR i=0 TO size-2
  DO
    minpos=i
    FOR j=i+1 TO size-1
    DO
      IF Compare(a(minpos),a(j))>0 THEN
        minpos=j
      FI
    OD
    
    IF minpos#i THEN
      tmp=a(i)
      a(i)=a(minpos)
      a(minpos)=tmp
    FI
  OD
RETURN

PROC Main()
  DEFINE SIZE="6"
  PTR ARRAY a(SIZE)

  a(0)="1.3.6.1.4.1.11.2.17.19.3.4.0.10"
  a(1)="1.3.6.1.4.1.11.2.17.5.2.0.79"
  a(2)="1.3.6.1.4.1.11.2.17.19.3.4.0.4"
  a(3)="1.3.6.1.4.1.11150.3.4.0.1"
  a(4)="1.3.6.1.4.1.11.2.17.19.3.4.0.1"
  a(5)="1.3.6.1.4.1.11150.3.4.0"

  PrintE("Array before sort:")
  PrintArray(a,SIZE) PutE()
  
  Sort(a,SIZE)
  PrintE("Array after sort:")
  PrintArray(a,SIZE)
RETURN
Output:

Screenshot from Atari 8-bit computer

Array before 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

Array after 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

Ada[edit]

Works with: Ada version 2012
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;
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[edit]

Vanilla[edit]

Place the call to the sort handler in a considering numeric strings statement.

(* 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
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[edit]

Use the localizedStandardCompare: string comparison method.

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
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[edit]

As a composition of pure functions:

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

ATS[edit]

(* Sort a list of object identifiers (OID). *)

#include "share/atspre_staload.hats"
staload UN = "prelude/SATS/unsafe.sats"

%{^
#include <alloca.h>  /* ATS2 exceptions require alloca(3). */
%}

#define NIL list_nil ()
#define ::  list_cons

infixl ( :: ) :::
#define VNIL list_vt_nil ()
#define :::  list_vt_cons

exception Exc_not_an_oid of (string, string)
exception Exc_internal_error of (string)
macdef not_an_oid (s) = $raise Exc_not_an_oid ($mylocation, ,(s))
macdef internal_error () = $raise Exc_internal_error ($mylocation)

fn
oid_explode
          {n : int}
          (s : string n)
    :<!exnwrt> List0_vt String1 =
  let
    fun
    loop      {i : nat | i <= n}
              .<n - i>.
              (lst : &list_vt (String1, 0) >> List0_vt String1,
               i   : size_t i)
        :<!exnwrt> void =
      if ~string_is_atend (s, i) then
        let
          fun
          scan_number
                    {j : int | i <= j; j <= n}
                    .<n - j>.
                    (j : size_t j)
              :<> [j : int | i <= j; j <= n]
                  size_t j =
            if string_is_atend (s, j) then
              j
            else if ~isdigit s[j] then
              j
            else
              scan_number (succ j)

          val j = scan_number i
        in
          if j = i then
            not_an_oid s
          else if string_is_atend (s, j) then
            let
              val t =
                strnptr2string (string_make_substring (s, i, j - i))
              val+ ~ VNIL = lst
            in
              lst := t ::: VNIL
            end
          else if s[j] <> '.' then
            not_an_oid s
          else if string_is_atend (s, succ j) then
            not_an_oid s
          else
            let
              val+ ~ VNIL = lst
              val t =
                strnptr2string
                  (string_make_substring (s, i, j - i))
              val () = lst := t ::: VNIL
              val+ @ _ ::: tail = lst
              val () = loop (tail, succ j)
              prval () = fold@ lst
            in
            end
        end

    prval () = lemma_string_param s

    var lst : List0_vt String1 = VNIL
  in
    if string_is_empty s then
      not_an_oid s;
    loop (lst, i2sz 0);
    lst
  end

fn
oid_field_cmp
          (x : String0,
           y : String0)
    :<!exn> int =
  let
    fun
    to_number {n : nat}
              {i : nat | i <= n}
              .<n - i>.
              (s     : string n,
               i     : size_t i,
               accum : ullint)
        :<!exn> ullint =
      if string_is_atend (s, i) then
        accum
      else if ~isdigit s[i] then
        internal_error ()
      else
        let
          val digit = $UN.cast{ullint} s[i] - $UN.cast '0'
        in
          to_number (s, succ i, (10ULL * accum) + digit)
        end

    val nx = to_number (x, i2sz 0, 0ULL)
    and ny = to_number (y, i2sz 0, 0ULL)
  in
    compare (nx, ny)
  end

fn
oid_cmp   (x : String0,
           y : String0)
    :<!exnwrt> int =
  let
    fun
    loop      {m, n : nat}
              .<m, n>.
              (xs : list (String1, m),
               ys : list (String1, n))
        :<!exnwrt> int =
      case+ ys of
      | NIL => if iseqz xs then 0 else 1
      | yhead :: ytail =>
        begin
          case+ xs of
          | NIL => ~1
          | xhead :: xtail =>
            let
              val cmp = oid_field_cmp (xhead, yhead)
            in
              if cmp = 0 then
                loop (xtail, ytail)
              else
                cmp
            end
        end

    val xs = list_vt2t (oid_explode x)
    and ys = list_vt2t (oid_explode y)
  in
    loop (xs, ys)
  end

fn
oid_list_sort
          {n   : int}
          (lst : list (String0, n))
    :<!exnwrt> list_vt (String0, n) =
  list_mergesort_fun<String0>
    (lst, lam (x, y) => $effmask_all oid_cmp (x, y))

implement
main0 () =
  try
    let
      val 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")
      val oid_sorted = list_vt2t (oid_list_sort oid_list)

      var p : List0 String0
    in
      for (p := oid_sorted; isneqz p; p := list_tail p)
        println! (list_head p)
    end
  with
  | ~ Exc_not_an_oid (loc, s) =>
    begin
      println! ("Not a UID: \"", s, "\" (exception raised at ", loc, ")");
      exit 1
    end
  | ~ Exc_internal_error loc =>
    begin
      println! ("Internal error (exception raised at ", loc, ")");
      exit 1
    end
Output:
$ patscc -DATS_MEMALLOC_GCBDW oid_sort.dats -lgc && ./a.out
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[edit]

; 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
}
Examples:
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
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[edit]

# syntax: GAWK -f SORT_A_LIST_OF_OBJECT_IDENTIFIERS.AWK
#
# sorting:
#   PROCINFO["sorted_in"] is used by GAWK
#   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"
#   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)
}
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]

A C99 (or later) compiler is required.

#include <stdio.h>
#include <stdlib.h>
#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;
}
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]

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


Clojure[edit]

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.

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

Factor[edit]

Factor provides the human<=> word which converts numbers in a string to integers before comparing them.

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
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æ[edit]

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

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.

Java[edit]

Works with: Java version 8 or higher
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);
    }
}
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[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"
]

Julia[edit]

Works with: Julia version 0.6
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)
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[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

Using Coroutine[edit]

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

M2000 Interpreter[edit]

In this example we have to change dot to #, to make each number as an integer one.

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
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[edit]

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


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

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

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

Using QuickSort[edit]

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;


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

Mathematica / Wolfram Language[edit]

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]]
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[edit]

OID as distinct string[edit]

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:

import algorithm, sequtils, strutils

type OID = distinct string

# 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

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[edit]

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

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

Same as with the previous method.

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;

Phix[edit]

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[edit]

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[edit]

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

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

Prolog[edit]

Works with: SWI 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).
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.

Raku[edit]

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

.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, <...>;

REXX[edit]

This REXX version supports negative integers in the OID.

/*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
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]

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


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

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

Or, using the Gem module (which knows about versions):

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

with identical output.

Rust[edit]

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

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

Swift[edit]

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

VBScript[edit]

' 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)
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[edit]

Library: Wren-fmt
Library: Wren-sort
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"))
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[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