Sort using a custom comparator: Difference between revisions
Added Oz example. |
→{{header|Slate}}: wrapping the code |
||
Line 870: | Line 870: | ||
<lang scheme>("strings" "sample" "sorted" "here" "Some" "are" "be" "to")</lang> |
<lang scheme>("strings" "sample" "sorted" "here" "Some" "are" "be" "to")</lang> |
||
=={{header|Slate}}== |
=={{header|Slate}}== |
||
<div style="width:100%;overflow:scroll"> |
|||
<lang slate>define: #words -> #('here' 'are' 'some' 'sample' 'strings' 'to' 'sort' 'since' 'this' 'exercise' 'is' 'not' 'really' 'all' 'that' 'dumb' '(sorry)'). |
<lang slate>define: #words -> #('here' 'are' 'some' 'sample' 'strings' 'to' 'sort' 'since' 'this' 'exercise' 'is' 'not' 'really' 'all' 'that' 'dumb' '(sorry)'). |
||
words sortBy: [| :first :second | (first lexicographicallyCompare: second) isNegative]</lang> |
words sortBy: [| :first :second | (first lexicographicallyCompare: second) isNegative]</lang> |
||
</div> |
|||
=={{header|Smalltalk}}== |
=={{header|Smalltalk}}== |
Revision as of 03:57, 26 January 2010
You are encouraged to solve this task according to the task description, using any language you may know.
Sort an array (or list) of strings in order of descending length, and in ascending lexicographic order for strings of equal length. Use a sorting facility provided by the language/library, combined with your own callback comparison function.
Note: Lexicographic order is case-insensitive.
Ada
Comparator_Package.ads
<lang ada>package Comparator_Package is
procedure Move_String(From : Natural; To : Natural); function Len (Left, Right : Natural) return Boolean; function Lt (Left, Right : Natural) return Boolean; procedure Print_Array;
end Comparator_Package;</lang>
Comparator_Package.adb
<lang ada>with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_Io; use Ada.Text_Io; with Ada.Characters.Handling; use Ada.Characters.Handling;
package body Comparator_Package is
type Data is array(Natural range <>) of Unbounded_String; Strings : Data := (Null_Unbounded_String, To_Unbounded_String("this"), To_Unbounded_String("is"), To_Unbounded_String("a"), To_Unbounded_String("set"), To_Unbounded_String("of"), To_Unbounded_String("strings"), To_Unbounded_String("to"), To_Unbounded_String("sort")); procedure Move_String(From : Natural; To : Natural) is begin Strings(To) := Strings(From); end Move_String; function Len (Left, Right : Natural) return Boolean is begin return Length(Strings(Left)) > Length(Strings(Right)); end Len; function Lt (Left, Right : Natural) return Boolean is begin return To_Lower(To_String(Strings(Left))) < To_Lower(To_String(Strings(Right))); end Lt; procedure Print_Array is begin for I in 1..Strings'Last loop Put_Line(To_String(Strings(I))); end loop; end Print_Array;
end Comparator_Package;</lang>
Custom_Comparator.adb
<lang ada>with Gnat.Heap_Sort_A; use Gnat.Heap_Sort_A; with Ada.Text_Io; use Ada.Text_Io; with Comparator_Package; use Comparator_Package;
procedure Custom_Comparator is begin
Put_Line(" Unsorted Array:"); Print_Array; New_Line; Put_Line(" Sorted in descending length:"); Sort(8, Move_String'access, Len'access); Print_Array; New_Line; Put_Line(" Sorted in Ascending order:"); Sort(8, Move_String'access, Lt'access); Print_Array;
end Custom_Comparator;</lang>
Output File
Unsorted Array: this is a set of strings to sort Sorted in descending length: strings sort this set to is of a Sorted in Ascending order: a is of set sort strings this to
AutoHotkey
<lang AutoHotkey>numbers = 5,3,7,9,1,13,999,-4 strings = Here,are,some,sample,strings,to,be,sorted Sort, numbers, F IntegerSort D, Sort, strings, F StringLengthSort D, msgbox % numbers msgbox % strings
IntegerSort(a1, a2) { return a2 - a1 }
StringLengthSort(a1, a2){ return strlen(a1) - strlen(a2) }</lang>
C
<lang c>#include <stdlib.h> /* for qsort */
- include <string.h> /* for strlen */
- include <strings.h> /* for strcasecmp */
int mycmp(const void *s1, const void *s2) {
int d; const char *l = *(const char **)s1, *r = *(const char **)s2; if (d = strlen(r) - strlen(l)) return d; return strcasecmp(l, r);
}
int main() {
const char *strings[8] = {"Here", "are", "some", "sample", "strings", "to", "be", "sorted"}; qsort(strings, 8, sizeof(const char *), mycmp); return 0;
}</lang>
C++
<lang cpp>#include <algorithm>
- include <string>
- include <cctype>
// compare character case-insensitive bool icompare_char(char c1, char c2) {
return std::toupper(c1) < std::toupper(c2);
}
// return true if s1 comes before s2 bool compare(std::string const& s1, std::string const& s2) {
if (s1.length() > s2.length()) return true; if (s1.length() < s2.length()) return false; return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), icompare_char);
}
int main() {
const std::string strings[8] = {"Here", "are", "some", "sample", "strings", "to", "be", "sorted"}; std::sort(strings, strings+8, compare); return 0;
}</lang>
C#
C# allows you to specify a custom compare to the built in sort method on a list
<lang csharp>using System; using System.Collections.Generic;
namespace RosettaCode {
class SortCustomComparator { // Driver program public void CustomSort() { String[] items = { "Here", "are", "some", "sample", "strings", "to", "be", "sorted" }; List<String> list = new List<string>(items);
DisplayList("Unsorted", list); list.Sort(CustomCompare); DisplayList("Descending Length", list);
list.Sort(); DisplayList("Ascending order", list); }
// Custom compare public int CustomCompare(String x, String y) { int result = x.Length.CompareTo(y.Length) * -1; if (result == 0) { result = x.ToLower().CompareTo(y.ToLower()); }
return result; }
// Output routine public void DisplayList(String header, List<String> theList) { Console.WriteLine(header); Console.WriteLine("".PadLeft(header.Length, '*')); foreach (String str in theList) { Console.WriteLine(str); } Console.WriteLine(); } }
}</lang>
Output File
Unsorted ******** Here are some sample strings to be sorted Descending Length ***************** strings sample sorted Here some are be to Ascending order *************** are be Here sample some sorted strings to
Clean
<lang clean>import StdEnv
less s1 s2
| size s1 > size s2 = True | size s1 < size s2 = False | otherwise = lower s1 < lower s2
where
lower :: String -> String lower s = {toLower c \\ c <-: s}
Start = sortBy less ["This", "is", "a", "set", "of", "strings", "to", "sort"]</lang>
Clojure
Clojure's sort function has a 2-argument version where the first argument is (like) a java.util.Comparator (in that it returns <0, 0, >0 for <,=,> comparisons respectively), and the second is the collection to be sorted. Thus the heart of this version is a comparator function that satisfies the problem spec. <lang lisp>(defn rosetta-compare [s1 s2]
(let [len1 (count s1), len2 (count s2)] (if (= len1 len2) (compare (.toLowerCase s1) (.toLowerCase s2)) (- len2 len1))))</lang>
Here it is in action: <lang lisp>(sort rosetta-compare items)</lang>
Common Lisp
In Common Lisp, the sort function takes a "less than" predicate that is used as the comparator. This parameter can be any two-argument function. Note: Common Lisp's sort function is destructive; for lists you should not use the original list afterwards, you should only use the return value.
For example, to sort strings case-insensitively in ascending order:
<lang lisp>CL-USER> (defvar *strings*
'("Cat" "apple" "Adam" "zero" "Xmas" "quit" "Level" "add" "Actor" "base" "butter"))
- STRINGS*
CL-USER> (sort *strings* #'string-lessp) ("Actor" "Adam" "add" "apple" "base" "butter" "Cat" "Level" "quit" "Xmas" "zero")</lang>
You can also provide an optional key function which maps each element to a key. The keys are then compared using the comparator. For example, to sort strings by length in descending order:
<lang lisp>CL-USER> (defvar *strings*
'("Cat" "apple" "Adam" "zero" "Xmas" "quit" "Level" "add" "Actor" "base" "butter"))
- STRINGS*
CL-USER> (sort *strings* #'> :key #'length) ("butter" "apple" "Level" "Actor" "Adam" "zero" "Xmas" "quit" "base"
"Cat" "add")</lang>
D
<lang d>module customsort ; import tango.io.Stdout ; import tango.text.Ascii ; // for lexi compare
// csort need the following 2 modules import tango.util.collection.ArraySeq ; import tango.util.collection.model.Comparator ; T[] csort(T)(inout T[] arr, int function(T, T) fn_cmp) {
ArraySeq!(T).quickSort(arr, 0, arr.length - 1, new class() Comparator!(T){ int compare(T a, T b) { return fn_cmp(a,b) ; } }) ; return arr ;
} int cmpLen(char[] a, char[] b) {
if (a.length < b.length) return 1 ; // longer string come first else if (a.length > b.length) return -1 ; return 0 ;
} int cmpLex(char[] a, char[] b) {
return icompare(a,b) ; // case-insensitive compare
} int cmpLenThenLex(char[] a, char[] b) { // in case misunderstood the task
return cmpLen(a,b) == 0 ? cmpLex(a,b) : cmpLen(a,b) ;
} void main() {
char[][] d = ["This", "is", "a", "set", "of", "strings", "to", "sort"]; Stdout(d.csort(&cmpLen)).newline ; // descending length char[][] a = ["BbCC","4321","cBBA","Abbc","1234","bBac","baCA","BAcC"] ; Stdout(a.csort(&cmpLex)).newline ; // ascending lexi order char[][] m = ["Bab","abbcc","baacc","Abbc","aAcc","abBac","bba","BAC"] ; Stdout(m.csort(&cmpLenThenLex)).newline ; // descending length then ascending lexi order
}</lang> Output:
[ strings, This, sort, set, of, is, to, a ] [ 1234, 4321, Abbc, baCA, BAcC, bBac, BbCC, cBBA ] [ abBac, abbcc, baacc, aAcc, Abbc, Bab, BAC, bba ]
E
<lang e>/** returns a if it is nonzero, otherwise b() */ def nonzeroOr(a, b) { return if (a.isZero()) { b() } else { a } }
["Here", "are", "some", "sample", "strings", "to", "be", "sorted"] \
.sort(fn a, b { nonzeroOr(b.size().op__cmp(a.size()), fn { a.compareToIgnoreCase(b) }) })</lang>
Factor
<lang factor>: my-compare ( s1 s2 -- <=> )
2dup [ length ] compare invert-comparison dup +eq+ = [ drop [ >lower ] compare ] [ 2nip ] if ;
{ "this" "is" "a" "set" "of" "strings" "to" "sort" } [ my-compare ] sort</lang>
Fortran
Fortran does not have builtin to sort arrays (of numbers or strings), with or without custom comparator; so we need modifying e.g. this code in order to handle strings and to accept a custom comparator.
<lang fortran>module sorts_with_custom_comparator
implicit none
contains
subroutine a_sort(a, cc) character(len=*), dimension(:), intent(inout) :: a interface integer function cc(a, b) character(len=*), intent(in) :: a, b end function cc end interface integer :: i, j, increment character(len=max(len(a), 10)) :: temp increment = size(a) / 2 do while ( increment > 0 ) do i = increment+1, size(a) j = i temp = a(i) do while ( j >= increment+1 .and. cc(a(j-increment), temp) > 0) a(j) = a(j-increment) j = j - increment end do a(j) = temp end do if ( increment == 2 ) then increment = 1 else increment = increment * 5 / 11 end if end do end subroutine a_sort
end module sorts_with_custom_comparator</lang>
Then we have to put our custom comparator in a module (to_lower is defined here):
<lang fortran>module comparators
implicit none
contains
integer function my_compare(a, b) character(len=*), intent(in) :: a, b
character(len=max(len(a),len(b))) :: a1, b1
a1 = a b1 = b call to_lower(b1) call to_lower(a1) if ( len(trim(a)) > len(trim(b)) ) then my_compare = -1 elseif ( len(trim(a)) == len(trim(b)) ) then if ( a1 > b1 ) then my_compare = 1 else my_compare = -1 end if else my_compare = 1 end if end function my_compare
end module comparators</lang>
At the end, we can test these:
<lang fortran>program CustomComparator
use comparators use sorts_with_custom_comparator implicit none
character(len=100), dimension(8) :: str integer :: i
str = (/ "this", "is", "an", "array", "of", "strings", "to", "sort" /) call a_sort(str, my_compare)
do i = 1, size(str) print *, trim(str(i)) end do
end program CustomComparator</lang>
Groovy
The "custom comparator" is just a closure attached to the sort method invocation. <lang groovy>def strings = [ "Here", "are", "some", "sample", "strings", "to", "be", "sorted" ] strings.sort { x, y -> x.compareToIgnoreCase(y)} println strings</lang>
Output:
["are", "be", "Here", "sample", "some", "sorted", "strings", "to"]
Haskell
<lang haskell>import List import Char
mycmp s1 s2 = case compare (length s2) (length s1) of
EQ -> compare (map toLower s1) (map toLower s2) x -> x
strings = ["Here", "are", "some", "sample", "strings", "to", "be", "sorted"] sorted = sortBy mycmp strings</lang>
Alternate definition of mycmp using the Monoid instance for Ordering:
<lang haskell>import Data.Monoid mycmp s1 s2 = mappend (compare (length s2) (length s1))
(compare (map toLower s1) (map toLower s2))</lang>
J
Case-insensitivity is obtained using lower, a verb taken from Change string case. Standard utilities tolower or toupper may be substituted.
<lang j> mycmp=: 1 :'/:u'
length_and_lex =: (-@:# ; lower)&> strings=: 'Here';'are';'some';'sample';'strings';'to';'be';'sorted' length_and_lex mycmp strings
+-------+------+------+----+----+---+--+--+ |strings|sample|sorted|Here|some|are|be|to| +-------+------+------+----+----+---+--+--+</lang>
Java
<lang java5>import java.util.Comparator; import java.util.Arrays;
public class Test {
public static void main(String[] args) { String[] strings = {"Here", "are", "some", "sample", "strings", "to", "be", "sorted"};
Arrays.sort(strings, new Comparator<String>() { public int compare(String s1, String s2) { int c = s2.length() - s1.length(); if (c == 0) c = s1.compareToIgnoreCase(s2); return c; } });
for (String s: strings) System.out.print(s + " "); }
}</lang>
JavaScript
<lang javascript>function lengthSorter(a, b) {
var result = b.length - a.length; if (result == 0) result = a.localeCompare(b); return result;
}
var test = ["Here", "are", "some", "sample", "strings", "to", "be", "sorted"]; test.sort(lengthSorter); alert( test.join(' ') ); // strings sample sorted Here some are be to</lang>
Mathematica
We define a new function to give true or false if two elements are in order. After that we can simply use the built-in Sort with an ordering function: <lang Mathematica>StringOrderQ[x_String, y_String] :=
If[StringLength[x] > StringLength[y], True, If[StringLength[x] < StringLength[y], False, OrderedQ[{x, y}] ] ]
words={"on","sunday","sander","sifted","and","sorted","sambaa","for","a","second"}; Sort[words,StringOrderQ[#1,#2]&]</lang> gives back: <lang Mathematica>{sambaa,sander,second,sifted,sorted,sunday,and,for,on,a}</lang>
MAXScript
<lang maxscript>fn myCmp str1 str2 = (
case of ( (str1.count < str2.count): 1 (str1.count > str2.count): -1 default:( -- String compare is case sensitive, name compare isn't. Hence... str1 = str1 as name str2 = str2 as name case of ( (str1 > str2): 1 (str1 < str2): -1 default: 0 ) ) )
)
strList = #("Here", "are", "some", "sample", "strings", "to", "be", "sorted") qSort strList myCmp print strList</lang>
Nial
<lang nial>sort fork [=[tally first,tally last],up, >= [tally first,tally last]] ['Here', 'are', 'some', 'sample', 'strings', 'to', 'be', 'sorted'] =+-------+------+------+----+----+---+--+--+ =|strings|sample|sorted|Here|some|are|be|to| =+-------+------+------+----+----+---+--+--+</lang>
Objective-C
<lang objc>#import <Foundation/Foundation.h>
@interface NSString (CustomComp) - (NSComparisonResult)my_compare: (id)obj; @end
- define esign(X) (((X)>0)?1:(((X)<0)?-1:0))
@implementation NSString (CustomComp) - (NSComparisonResult)my_compare: (id)obj {
int l = esign((int)([self length] - [obj length])); switch(l) { case(NSOrderedDescending): return NSOrderedAscending; // reverse the ordering case(NSOrderedAscending): return NSOrderedDescending; case(NSOrderedSame): return [self caseInsensitiveCompare: obj]; } return NSOrderedSame; // should never run this...
} @end
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *arr = [NSMutableArray arrayWithArray: [@"this is a set of strings to sort" componentsSeparatedByString: @" "] ];
[arr sortUsingSelector: @selector(my_compare:)];
NSEnumerator *iter = [arr objectEnumerator]; NSString *str; while( (str = [iter nextObject]) != nil ) { NSLog(@"%@", str); }
[pool release]; return EXIT_SUCCESS;
}</lang>
This example can also be written using sort descriptors:
<lang objc>#import <Foundation/Foundation.h>
int main() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *strings = [@"Here are some sample strings to be sorted" componentsSeparatedByString:@" "];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO]; NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:@"lowercaseString" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil]; [sd1 release]; [sd2 release];
NSArray *sorted = [strings sortedArrayUsingDescriptors:sortDescriptors]; NSLog(@"%@", sorted);
[pool release];
return 0;
}</lang>
OCaml
<lang ocaml>let mycmp s1 s2 =
if String.length s1 <> String.length s2 then compare (String.length s2) (String.length s1) else String.compare (String.lowercase s1) (String.lowercase s2)</lang>
List: <lang ocaml># let strings = ["Here"; "are"; "some"; "sample"; "strings"; "to"; "be"; "sorted"];; val strings : string list =
["Here"; "are"; "some"; "sample"; "strings"; "to"; "be"; "sorted"]
- List.sort mycmp strings;;
- : string list = ["strings"; "sample"; "sorted"; "Here"; "some"; "are"; "be"; "to"]</lang>
Array: <lang ocaml># let strings = [|"Here"; "are"; "some"; "sample"; "strings"; "to"; "be"; "sorted"|];; val strings : string array =
[|"Here"; "are"; "some"; "sample"; "strings"; "to"; "be"; "sorted"|]
- Array.sort mycmp strings;;
- : unit = ()
- strings;;
- : string array = [|"strings"; "sample"; "sorted"; "Here"; "some"; "are"; "be"; "to"|]</lang>
Oz
<lang oz>declare
fun {LexicographicLessThan Xs Ys} for X in {Map Xs Char.toLower} Y in {Map Ys Char.toLower} return:Return default:{Length Xs}<{Length Ys} do if X < Y then {Return true} end end end
fun {LessThan Xs Ys} {Length Xs} == {Length Ys} andthen {LexicographicLessThan Xs Ys} orelse {Length Xs} < {Length Ys} end
Strings = ["Here" "are" "some" "sample" "strings" "to" "be" "sorted"]
in
{ForAll {Sort Strings LessThan} System.showInfo}</lang>
Perl
<lang perl>sub mycmp { length $b <=> length $a or lc $a cmp lc $b }
my @strings = ("Here", "are", "some", "sample", "strings", "to", "be", "sorted"); my @sorted = sort mycmp @strings;</lang>
Or inline: <lang perl>my @strings = qw/here are some sample strings to be sorted/; my @sorted = sort {length $b <=> length $a or lc $a cmp lc $b} @strings</lang>
Faster with a Schwartzian transform: <lang perl>my @strings = qw/here are some sample strings to be sorted/; my @sorted = map { $$_[0] }
sort { $$a[1] <=> $$b[1] or $$a[2] cmp $$b[2] } map { [ $_, length, lc ] } @strings;</lang>
PHP
<lang php><?php function mycmp($s1, $s2) {
if ($d = strlen($s2) - strlen($s1)) return $d; return strcasecmp($s1, $s2);
}
$strings = array("Here", "are", "some", "sample", "strings", "to", "be", "sorted"); usort($strings, "mycmp"); ?></lang>
PL/I
Platform: WIN <lang pli>MRGEPKG: package exports(MERGESORT,MERGE,RMERGE);
DCL (T(4)) CHAR(20) VAR; /* scratch space of length N/2 */
MERGE: PROCEDURE (A,LA,B,LB,C,CMPFN);
DECLARE (A(*),B(*),C(*)) CHAR(*) VAR; DECLARE (LA,LB) FIXED BIN(31) NONASGN; DECLARE (I,J,K) FIXED BIN(31); DECLARE CMPFN ENTRY( NONASGN CHAR(*) VAR, NONASGN CHAR(*) VAR) RETURNS (FIXED bin(31)); I=1; J=1; K=1; DO WHILE ((I <= LA) & (J <= LB)); IF CMPFN(A(I),B(J)) <= 0 THEN DO; C(K)=A(I); K=K+1; I=I+1; END; ELSE DO; C(K)=B(J); K=K+1; J=J+1; END; END; DO WHILE (I <= LA); C(K)=A(I); I=I+1; K=K+1; END; return;
END MERGE;
MERGESORT: PROCEDURE (A,N,CMPFN) RECURSIVE ;
DECLARE (A(*)) CHAR(*) VAR; DECLARE N FIXED BINARY(31) NONASGN; DECLARE CMPFN ENTRY( NONASGN CHAR(*) VAR, NONASGN CHAR(*) VAR) RETURNS (FIXED bin(31)); DECLARE (M,I) FIXED BINARY; DECLARE AMP1(N) CHAR(20) VAR BASED(P); DECLARE P POINTER;
IF (N=1) THEN RETURN; M = trunc((N+1)/2); IF M > 1 THEN CALL MERGESORT(A,M,CMPFN); P=ADDR(A(M+1)); IF (N-M > 1) THEN CALL MERGESORT(AMP1,N-M,CMPFN); IF CMPFN(A(M),AMP1(1)) <= 0 THEN RETURN; DO I=1 to M; T(I)=A(I); END; CALL MERGE(T,M,AMP1,N-M,A,CMPFN);
END MERGESORT;
RMERGE: PROC OPTIONS(MAIN); DCL I FIXED BIN(31); DCL A(8) CHAR(20) VAR INIT("this","is","a","set","of","strings","to","sort");
MyCMP: PROCEDURE(A,B) RETURNS (FIXED BIN(31));
DCL (A,B) CHAR(*) VAR NONASGN; DCL (I,J) FIXED BIN(31);
I = length(trim(A)); J = length(trim(B)); IF I < J THEN RETURN(+1); IF I > J THEN RETURN(-1); IF lowercase(A) < lowercase(B) THEN RETURN(-1); IF lowercase(A) > lowercase(B) THEN RETURN(+1); RETURN (0);
END MyCMP;
CALL MERGESORT(A,8,MyCMP); DO I=1 TO 8;
put edit (I,A(I)) (F(5),X(2),A(10)) skip;
END;
put skip; END RMERGE;</lang>
Pop11
<lang pop11>lvars ls = ['Here' 'are' 'some' 'sample' 'strings' 'to' 'be' 'sorted']; define compare(s1, s2); lvars k = length(s2) - length(s1); if k < 0 then
return(true);
elseif k > 0 then
return(false);
else
return (alphabefore(uppertolower(s1), uppertolower(s2)));
endif; enddefine;
syssort(ls, compare) -> ls;
NOTE: The definition of compare can also be written thus: define compare(s1, s2);
lvars l1 = length(s1), l2 = length(s2); l1 > l2 or (l1 == l2 and alphabefore(uppertolower(s1), uppertolower(s2)))
enddefine;</lang>
PowerShell
The Sort-Object
cmdlet accepts script blocks as arguments as well as multiple criteria after which to sort.
<lang powershell>$list = "Here", "are", "some", "sample", "strings", "to", "be", "sorted"
$list | Sort-Object {-$_.Length},{$_}</lang>
The negated string length is the first sort criterion, the second is the string itself, resulting in descending length and ascending lexicographic order.
Python
Using a key function is usually more efficient than a comparator. We can take advantage of the fact that tuples are ordered first by the first element, then by the second, etc., to perform a sort on multiple criteria. <lang python>strings = "here are Some sample strings to be sorted".split()
def mykey(x):
return -len(x), x.upper()
print sorted(strings, key=mykey)</lang>
Sample output: <lang python>['strings', 'sample', 'sorted', 'here', 'Some', 'are', 'be', 'to']</lang>
Alternative method using cmp
To technically comply with this task, we can also use an actual comparator (cmp) function which will be called every time members of the original list are to be compared. Note that this feature is worse than using the key argument and has been removed from Python 3, so should no longer be used in new code. <lang python>def mycmp(s1, s2):
return cmp(len(s2), len(s1)) or cmp(s1.upper(), s2.upper())
print sorted(strings, cmp=mycmp)</lang>
Ruby
Since Ruby 1.8.6 Enumerables have a "sort_by" method, taking a key block, which is more efficient than a comparator. We can take advantage of the fact that Arrays are ordered first by the first element, then by the second, etc., to perform a sort on multiple criteria.
<lang ruby>words = %w(Here are some sample strings to be sorted) p words.sort_by {|word| [-word.size, word.downcase]}</lang>
To technically comply with this task, we can also use an actual comparator block which will be called every time members of the original list are to be compared. <lang ruby>p words.sort {|a, b| d = b.size <=> a.size
d != 0 ? d : a.upcase <=> b.upcase}</lang>
Scheme
<lang scheme>(use srfi-13);;Syntax for module inclusion depends on implementation,
- as does the presence of a sort function.
(define (mypred? a b)
(let ((len-a (string-length a))
(len-b (string-length b)))
(if (= len-a len-b)
(string>? (string-downcase b) (string-downcase a))
(> len-a len-b))))
(sort '("sorted" "here" "strings" "sample" "Some" "are" "be" "to") mypred?) </lang> Sample output: <lang scheme>("strings" "sample" "sorted" "here" "Some" "are" "be" "to")</lang>
Slate
<lang slate>define: #words -> #('here' 'are' 'some' 'sample' 'strings' 'to' 'sort' 'since' 'this' 'exercise' 'is' 'not' 'really' 'all' 'that' 'dumb' '(sorry)'). words sortBy: [| :first :second | (first lexicographicallyCompare: second) isNegative]</lang>
Smalltalk
<lang smalltalk>#('here' 'are' 'some' 'sample' 'strings' 'to' 'sort' 'since' 'this' 'exercise' 'is' 'not' 'really' 'all' 'that' 'dumb' '(sorry)' ) asSortedCollection
sortBlock: [:first :second | (second size = first size) ifFalse: [second size < first size] ifTrue: [first < second]]</lang>
Standard ML
List:
<lang sml>fun mygt (s1, s2) =
if size s1 <> size s2 then size s2 > size s1 else String.map Char.toLower s1 > String.map Char.toLower s2</lang>
<lang sml>- val strings = ["Here", "are", "some", "sample", "strings", "to", "be", "sorted"]; val strings = ["Here","are","some","sample","strings","to","be","sorted"]
: string list
- ListMergeSort.sort mygt strings; val it = ["strings","sample","sorted","Here","some","are","be","to"]
: string list</lang>
Array:
<lang sml>fun mycmp (s1, s2) =
if size s1 <> size s2 then Int.compare (size s2, size s1) else String.compare (String.map Char.toLower s1, String.map Char.toLower s2)</lang>
<lang sml>- val strings = Array.fromList ["Here", "are", "some", "sample", "strings", "to", "be", "sorted"]; val strings = [|"Here","are","some","sample","strings","to","be","sorted"|]
: string array
- ArrayQSort.sort mycmp strings; val it = () : unit - strings; val it = [|"strings","sample","sorted","Here","some","are","be","to"|]
: string array</lang>
Tcl
<lang tcl>proc sorter {a b} {
set la [string length $a] set lb [string length $b] if {$la < $lb} { return 1 } elseif {$la > $lb} { return -1 } return [string compare [string tolower $a] [string tolower $b]]
}
set strings {here are Some sample strings to be sorted} lsort -command sorter $strings ;# ==> strings sample sorted here Some are be to</lang>
Visual Basic .NET
<lang vbnet>Imports System
Module Sorting_Using_a_Custom_Comparator
Function CustomComparator(ByVal x As String, ByVal y As String) As Integer Dim result As Integer result = y.Length - x.Length If result = 0 Then result = String.Compare(x, y, True) End If Return result End Function
Sub Main() Dim strings As String() = {"test", "Zoom", "strings", "a"}
Array.Sort(strings, New Comparison(Of String)(AddressOf CustomComparator)) End Sub
End Module</lang>
Ursala
A standard library function, psort, takes a list of binary relational predicates and returns a function that uses them in order of decreasing priority to perform a sort. The less or equal length predicate (leql) and lexically less or equal predicate (lleq) are also standard library functions. This task is therefore easily dispatched as shown. <lang Ursala>#import std
- show+
data = <'this','is','a','list','of','strings','to','be','sorted'>
example = psort<not leql,lleq+ ~* ~&K31K30piK26 letters> data</lang> The lleq library function is case sensitive, so it is composed with a function to convert the words to lower case on the fly (without destructively modifying them) in order to meet the task requirement of case insensitivity.
output:
strings sorted list this be is of to a
- Programming Tasks
- Sorting
- Ada
- Ada examples needing attention
- Examples needing attention
- AutoHotkey
- C
- C++
- C sharp
- Clean
- Clojure
- Common Lisp
- D
- Tango
- E
- Factor
- Fortran
- Groovy
- Haskell
- J
- Java
- JavaScript
- Mathematica
- MAXScript
- Nial
- Objective-C
- OCaml
- Oz
- Perl
- PHP
- PL/I
- Pop11
- PowerShell
- Python
- Ruby
- Scheme
- Slate
- Smalltalk
- Standard ML
- Tcl
- Visual Basic .NET
- Ursala