Compare a list of strings

From Rosetta Code
Revision as of 19:28, 6 October 2016 by PureFox (talk | contribs) (Added FreeBASIC)
Task
Compare a list of strings
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Given a   list   of arbitrarily many strings, show how to:

  •   test if they are all lexically equal
  •   test if every string is lexically less than the one after it (i.e. whether the list is in strict ascending order)


Each of those two tests should result in a single true or false value, which could be used as the condition of an   if   statement or similar.

If the input list has less than two elements, the tests should always return true.

There is no need to provide a complete program and output.

Assume that the strings are already stored in an array/list/sequence/tuple variable (whatever is most idiomatic) with the name   strings,   and just show the expressions for performing those two tests on it (plus of course any includes and custom functions etc. that it needs),   with as little distractions as possible.

Try to write your solution in a way that does not modify the original list,   but if it does then please add a note to make that clear to readers.

If you need further guidance/clarification,   see #Perl and #Python for solutions that use implicit short-circuiting loops,   and #Perl_6 for a solution that gets away with simply using a built-in language feature.


Related tasks



Ada

We will store the "list" of strings in a vector. The vector will hold "indefinite" strings, i.e., the strings can have different lengths. <lang Ada> package String_Vec is new Ada.Containers.Indefinite_Vectors

    (Index_Type => Positive, Element_Type => String);
  
  use type String_Vec.Vector;</lang>

The equality test iterates from the first to the last-but one index. For index Idx, it checks checks if Strings(Idx) and Strings(Idx+1) are different. If the answer is yes for any Idx, the function immediately returns False. If the answer is no for all Idx, the function finally returns True. <lang Ada> function All_Are_The_Same(Strings: String_Vec.Vector) return Boolean is

  begin
     for Idx in Strings.First_Index .. Strings.Last_Index-1 loop

if Strings(Idx) /= Strings(Idx+1) then return False; end if;

     end loop;
     return True;
  end All_Are_The_Same;</lang>

Similarily, the strictly ascending test checks if Strings(Idx) is greater or equal Strings(Idx+1). <lang Ada> function Strictly_Ascending(Strings: String_Vec.Vector) return Boolean is

  begin
     for Idx in Strings.First_Index+1 .. Strings.Last_Index loop

if Strings(Idx-1) >= Strings(Idx) then return False; end if;

     end loop;
     return True;
  end Strictly_Ascending;</lang>

If the variable Strings is of the type String_Vec.vector, one can call these two functions as usual. <lang Ada>Put_Line(Boolean'Image(All_Are_The_Same(Strings)) & ", " &

        Boolean'Image(Strictly_Ascending(Strings)));</lang>

If Strings holds two or more strings, the result will be either of TRUE, FALSE, or FALSE, TRUE, or FALSE, FALSE, indicating all strings are the same, or they are strictly ascending, or neither.

However, if Strings only holds zero or one string, the result will be TRUE, TRUE.

ALGOL 68

<lang ALGOL68>[]STRING list1 = ("AA","BB","CC"); []STRING list2 = ("AA","AA","AA"); []STRING list3 = ("AA","CC","BB"); []STRING list4 = ("AA","ACB","BB","CC"); []STRING list5 = ("single_element");

[][]STRING all lists to test = (list1, list2, list3, list4, list5);

PROC equal = ([]STRING list) BOOL:

  BEGIN
     BOOL ok := TRUE;
     FOR i TO UPB list - 1 WHILE ok DO

ok := list[i] = list[i+1]

     OD;
     ok
  END;

PROC less than = ([]STRING list) BOOL:

  BEGIN
     BOOL ok := TRUE;
     FOR i TO UPB list - 1 WHILE ok DO

ok := list[i] < list[i + 1]

     OD;
     ok
  END;

FOR i TO UPB all lists to test DO

  []STRING list = all lists to test[i];
  print (("list:", (STRING s; FOR i TO UPB list DO s +:= " " + list[i] OD; s), new line));
  IF equal (list) THEN
     print (("...is lexically equal", new line))
  ELSE
     print (("...is not lexically equal", new line))
  FI;
  IF less than (list) THEN
     print (("...is in strict ascending order", new line))
  ELSE
     print (("...is not in strict ascending order", new line))
  FI

OD</lang>

Output:
list: AA BB CC
...is not lexically equal
...is in strict ascending order
list: AA AA AA
...is lexically equal
...is not in strict ascending order
list: AA CC BB
...is not lexically equal
...is not in strict ascending order
list: AA ACB BB CC
...is not lexically equal
...is in strict ascending order
list: single_element
...is lexically equal
...is in strict ascending order


AppleScript

Translation of: JavaScript


<lang AppleScript>-- allEqual :: [String] -> Bool on allEqual(xs)

   _and(zipWith(my _equal, xs, rest of xs))

end allEqual

-- azSorted :: [String] -> Bool on azSorted(xs)

   _and(zipWith(my azBeforeOrSame, xs, rest of xs))

end azSorted

-- _equal :: a -> a -> Bool on _equal(a, b)

   a = b

end _equal

-- azBefore :: String -> String -> Bool on azBeforeOrSame(a, b)

   a ≥ b

end azBeforeOrSame

-- _and :: [a] -> Bool on _and(xs)

   foldr(_equal, true, xs)

end _and


-- TEST on run

   set lstA to ["isiZulu", "isiXhosa", "isiNdebele", "Xitsonga", "Tshivenda", ¬
       "Setswana", "Sesotho sa Leboa", "Sesotho", "English", "Afrikaans"]
   
   set lstB to ["Afrikaans", "English", "Sesotho", "Sesotho sa Leboa", "Setswana", ¬
       "Tshivenda", "Xitsonga", "isiNdebele", "isiXhosa", "isiZulu"]
   
   set lstC to ["alpha", "alpha", "alpha", "alpha", "alpha", "alpha", "alpha", ¬
       "alpha", "alpha", "alpha"]
   
   
   {allEqual:map(allEqual, [lstA, lstB, lstC]), azSorted:map(azSorted, [lstA, lstB, lstC])}
   
   -- > {allEqual:{false, false, true}, azSorted:{false, true, true}}

end run


-- GENERIC FUNCTIONS

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

   set mf to mReturn(f)
   set lng to length of xs
   set lst to {}
   repeat with i from 1 to lng
       set end of lst to mf's lambda(item i of xs, i, xs)
   end repeat
   return lst

end map

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

   set mf to mReturn(f)
   
   set v to startValue
   set lng to length of xs
   repeat with i from lng to 1 by -1
       set v to mf's lambda(v, item i of xs, i, xs)
   end repeat
   return v

end foldr

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

   set nx to length of xs
   set ny to length of ys
   
   if nx < 1 or ny < 1 then
       {}
   else
       set mf to mReturn(f)
       if nx < ny then
           set lng to nx
       else
           set lng to ny
       end if
       set lst to {}
       repeat with i from 1 to lng
           set end of lst to mf's lambda(item i of xs, item i of ys)
       end repeat
       return lst
   end if

end zipWith

-- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: Handler -> Script on mReturn(f)

   if class of f is script then
       f
   else
       script
           property lambda : f
       end script
   end if

end mReturn </lang>

Output:
{allEqual:{false, false, true}, azSorted:{false, true, true}}

AWK

<lang AWK>

  1. syntax: GAWK -f COMPARE_A_LIST_OF_STRINGS.AWK

BEGIN {

   main("AA,BB,CC")
   main("AA,AA,AA")
   main("AA,CC,BB")
   main("AA,ACB,BB,CC")
   main("single_element")
   exit(0)

} function main(list, arr,i,n,test1,test2) {

   test1 = 1 # elements are identical
   test2 = 1 # elements are in ascending order
   n = split(list,arr,",")
   printf("\nlist:")
   for (i=1; i<=n; i++) {
     printf(" %s",arr[i])
     if (i > 1) {
       if (arr[i-1] != arr[i]) {
         test1 = 0 # elements are not identical
       }
       if (arr[i-1] >= arr[i]) {
         test2 = 0 # elements are not in ascending order
       }
     }
   }
   printf("\n%d\n%d\n",test1,test2)

} </lang>

Output:
list: AA BB CC
0
1

list: AA AA AA
1
0

list: AA CC BB
0
0

list: AA ACB BB CC
0
1

list: single_element
1
1

C

<lang c>#include <stdio.h>

  1. include <string.h>

int strings_are_equal(char * * strings, int nstrings) {

 int result = 1;
 
 while (result && (--nstrings > 0))
 {
   result = !strcmp(*strings, *(strings+nstrings));
 }
 return result;

}

int strings_are_in_ascending_order(char * * strings, int nstrings) {

 int result = 1;
 int k = 0;
 
 while (result && (++k < nstrings))
 {
   result = (0 >= strcmp(*(strings+k-1), *(strings+k)));
 }
 return result;

}</lang>

C#

<lang csharp>public static bool[] CompareAListOfStrings(List<string> strings) {

   return strings.Count < 2 ? new [] { true, true }
       : new [] {
           strings.Distinct().Count() < 2,
           Enumerable.Range(1, strings.Count - 1).All(i => string.Compare(strings[i-1], strings[i]) < 0)
   };

}</lang>

C++

Assuming that the strings variable is of type T<std::string> where T is an ordered STL container such as std::vector:

Works with: C++ version 11

<lang cpp>#include <algorithm>

  1. include <string>

std::all_of( ++(strings.begin()), strings.end(),

            [&](std::string a){ return a == strings.front(); } )  // All equal

std::is_sorted( strings.begin(), strings.end(),

               [](std::string a, std::string b){ return !(b < a); }) )  // Strictly ascending</lang>

D

<lang d>void main() {

   import std.stdio, std.algorithm, std.range, std.string;
   foreach (const strings; ["AA AA AA AA", "AA ACB BB CC"].map!split) {
       strings.writeln;
       strings.zip(strings.dropOne).all!(ab => ab[0] == ab[1]).writeln;
       strings.zip(strings.dropOne).all!(ab => ab[0] < ab[1]).writeln;
       writeln;
   }

}</lang>

Output:
["AA", "AA", "AA", "AA"]
true
false

["AA", "ACB", "BB", "CC"]
false
true

Clojure

Used similar approach as the Python solution

<lang clojure>

Checks if all items in strings list are equal (returns true if list is empty)

(every? (fn a nexta (= a nexta)) (map vector strings (rest strings))))

Checks strings list is in ascending order (returns true if list is empty)

(every? (fn a nexta (<= (compare a nexta) 0)) (map vector strings (rest strings))))

</lang>

Common Lisp

<lang Lisp> (defun strings-equal-p (strings)

 (null (remove (first strings) (rest strings) :test #'string=)))

(defun strings-ascending-p (strings)

 (loop for string1 = (first strings) then string2
       for string2 in (rest strings)
       always (string-lessp string1 string2)))

</lang>

Elena

<lang elena>#import system.

  1. import system'collections.
  2. import system'routines.
  3. import extensions.
  1. class(extension)helper

{

   #method is &equal
       = $nil == self seek:(self firstMember) &eachPair:(:m:n) [ m equal:n not ].
       
   #method is &ascending
   [
       #var former := self enumerator.
       #var later := self enumerator.
       
       later next.
       
       ^ $nil == former zip:later &into:(:prev:next)[ next <= prev ] seek &each: b [ b ].
   ]

}

  1. symbol program =

[

   (("AA","BB","CC"),("AA","AA","AA"),("AA","CC","BB"),("AA","ACB","BB","CC"),List new += "single_element")
       run &each: list
       [
           console writeLine:list:" all equal - ":(list is &equal).
           console writeLine:list:" ascending - ":(list is &ascending).
       ].
       
   console readChar.            

]. </lang>

Elixir

<lang elixir>defmodule RC do

 def compare_strings(strings) do
   {length(Enum.uniq(strings))<=1, strict_ascending(strings)}
 end
 
 defp strict_ascending(strings) when length(strings) <= 1, do: true
 defp strict_ascending([first, second | _]) when first >= second, do: false
 defp strict_ascending([_, second | rest]), do: strict_ascending([second | rest])

end

lists = [ ~w(AA AA AA AA), ~w(AA ACB BB CC), ~w(AA CC BB), [], ["XYZ"] ] Enum.each(lists, fn list ->

 IO.puts "#{inspect RC.compare_strings(list)}\t<= #{inspect list} "

end)</lang>

Output:
{true, false}   <= ["AA", "AA", "AA", "AA"]
{false, true}   <= ["AA", "ACB", "BB", "CC"]
{false, false}  <= ["AA", "CC", "BB"]
{true, true}    <= []
{true, true}    <= ["XYZ"]

Erlang

Translation of: Haskell

<lang erlang> -module(compare_strings).

-export([all_equal/1,all_incr/1]).

all_equal(Strings) -> all_fulfill(fun(S1,S2) -> S1 == S2 end,Strings).

all_incr(Strings) -> all_fulfill(fun(S1,S2) -> S1 < S2 end,Strings).

all_fulfill(Fun,Strings) -> lists:all(fun(X) -> X end,lists:zipwith(Fun, lists:droplast(Strings), tl(Strings)) ). </lang>

F#

<lang fsharp>let allEqual strings = Seq.isEmpty strings || Seq.forall (fun x -> x = Seq.head strings) (Seq.tail strings) let ascending strings = Seq.isEmpty strings || Seq.forall2 (fun x y -> x < y) strings (Seq.tail strings)</lang>

Actually allEqual is a shortcut and ascending is a general pattern. We can make a function out of it which constructs a new function from a comparision function

<lang fsharp>let (!) f s = Seq.isEmpty s || Seq.forall2 f s (Seq.tail s)</lang>

and define the 2 task functions that way

<lang fsharp>let allEqual = !(=) let ascending = !(<)</lang>

getting something similar as the builtin in Perl 6

Forth

Raw Forth is a very low level language and has no Native lists so we have to build from scratch. Remarkably by concatenating these low level operations and using the simple Forth parser we can build the linked lists of strings and the list operators quite simply. The operators and lists that we create become extensions to the language. <lang>\ linked list of strings creators

," ( -- ) [CHAR] " WORD c@ 1+ ALLOT  ; \ Parse input stream until " and write into next available memory
[[ ( -- ) 0 C, ; \ begin a list. write a 0 into next memory byte (null string)
]] ( -- ) [[ ; \ end list with same null string
nth ( n list -- addr) swap 0 do count + loop ; \ return address of the Nth item in a list
items ( list -- n ) \ return the number of items in a list
          0
          begin
             1+ 2dup swap Nth C@
          0= until
          nip 1- ;
compare$ ( $1 $2 -- -n|0|n ) count rot count compare ; \ compare is an ANS Forth word. returns 0 if $1=$2
compare[] ( list n1 n2 -- flag) \ compare items n1 and n2 in list
           ROT dup >R nth ( -- $1)
           swap r> nth    ( -- $1 $2)
           compare$ ;

\ create our lexical operators

LEX= ( list -- flag)
          0                                                 \ place holder for the flag
          over items 1
          DO
             over I  I 1+ compare[] +                       \ we sum the comparison results on the stack
          LOOP
          nip 0= ;
LEX< ( list -- flag)
          0                                                 \ place holder for the flag
          over items 1
          DO
             over I  I 1+ compare[] 0< NOT +
          LOOP
          nip 0= ;

\ make some lists create strings ," ENTRY 4" ," ENTRY 3" ," ENTRY 2" ," ENTRY 1" create strings2 ," the same" ," the same" ," the same" create strings3 ," AAA" ," BBB" ," CCC" ," DDD" </lang>

Test at the Forth console (-1 is the result for TRUE)

STRINGS  lex= . 0 ok
STRINGS2 lex= . -1 ok
STRINGS3 lex= . 0 ok 
STRINGS  lex< . 0 ok
STRINGS2 lex< . 0 ok
STRINGS3 lex< . -1 ok

Fortran

Fortran does not offer a "string" item, which is to say, a sequence of items plus the length as one entity as in Pascal, among others. It does offer a CHARACTER variable, having some specified number of characters so the usual approach is to choose a length that is "long enough". In character comparisons, trailing spaces are ignored so that "xx" and "xx " are deemed equal. Similarly, it does not offer a list-of-thingies item, so again the usual approach is to provide an array of a size "long enough". One could develop a scheme with auxiliary counters stating how many elements are in use and so forth, but for this example, the parameterisation will do. Inspection of such arrays of character entities requires explicit DO-loops and IF-statements, and functions ALLINORDER and ALLEQUAL could be devised. Earlier Fortrans (prior to 77) lack a CHARACTER type, and so one must struggle with integer arrays.

Later Fortran (90 et seq) offers the special function ALL (and its associate, ANY) for testing multiple logical expressions, and also syntax allowing multiple elements of an array to be specified, as in A(3:7) to access elements 3, 4, 5, 6, 7 of array A. The ALL function has the special feature that if no logical expressions exist, then they, er, ... all ... are true and the result of ALL(nothing) is true. Well, none of them are false... Whatever the rationalisations this delivers the required result when the list has but one element and so there are no pairs to produce logical expressions, so, none of them are false, so the result is true, as specified.

On the other hand a function such as ALLINORDER would show the sound of one hand clapping. It would also reveal the order in which comparisons were made, and whether the loop would quit on the first failure or blockheadedly slog on through the lot regardless. Alas, on these questions the documentation for ALL is suspiciously silent.

<lang Fortran>

     INTEGER MANY,LONG
     PARAMETER (LONG = 6,MANY = 4)	!Adjust to suit.
     CHARACTER*(LONG) STRINGS(MANY)	!A list of text strings.
     STRINGS(1) = "Fee"
     STRINGS(2) = "Fie"
     STRINGS(3) = "Foe"
     STRINGS(4) = "Fum"
     IF (ALL(STRINGS(1:MANY - 1) .LT. STRINGS(2:MANY))) THEN
       WRITE (6,*) MANY," strings: strictly increasing in order."
      ELSE
       WRITE (6,*) MANY," strings: not strictly increasing in order."
     END IF
     IF (ALL(STRINGS(1:MANY - 1) .EQ. STRINGS(2:MANY))) THEN
       WRITE (6,*) MANY," strings: all equal."
      ELSE
       WRITE (6,*) MANY," strings: not all equal."
     END IF
     END

</lang>

And yes, if MANY is set to one and the extra texts are commented out, the results are both true, and ungrammatical statements are made. Honest. Possibly, another special function, as in COUNT(STRINGS(1:MANY - 1) .LT. STRINGS(2:MANY))) would involve less one-hand-clapping when there are no comparisons to make, but the production of a report that would use it is not in the specification.

F2003-F2008

F2008 standard ([ISO 2010], 4.4.3) defines the character variable of the character type as a set of values composed of character strings and a character string is a sequence of characters, numbered from left to right 1, 2, 3, ... up to the number of characters in the string. The number of characters in the string is called the length of the string. The length is a type parameter; its kind is processor dependent and its value is greater than or equal to zero. I.e in declaration <lang Fortran>

character (len=12) :: surname

</lang> keyword len is NOT a size of array, it is an intrinsic parameter of character type, and character type is in fortran a first-class type: they can be assigned as objects or passed as parameters to a subroutine.

In summary, the character data type in Fortran is a real, first class data type. Fortran character strings are not hacked-up arrays! <lang Fortran> program compare_char_list

  implicit none
  character(len=6), allocatable, dimension(:) :: ss
  integer :: many
  ss = ["Fee","Fie","Foe","Fum"]
  many = size(ss)
  if (all(ss(1:many - 1) .lt. ss(2:many))) then
     write (*,*) many," strings: strictly increasing in order."
  else
     write (*,*) many," strings: not strictly increasing in order."
  end if
  if (all(ss(1:many - 1) .eq. ss(2:many))) then
     write (*,*) many," strings: all equal."
  else
     write (*,*) many," strings: not all equal."
  end if

end program compare_char_list </lang>

FreeBASIC

<lang freebasic> ' FB 1.05.0 Win64

Function AllEqual(strings() As String) As Boolean

  Dim length As Integer = UBound(strings) - LBound(strings) + 1
  If length < 2 Then Return False
  For i As Integer = LBound(strings) + 1 To UBound(strings)
    If strings(i - 1) <> strings(i) Then Return False
  Next
  Return True

End Function

Function AllAscending(strings() As String) As Boolean

  Dim length As Integer = UBound(strings) - LBound(strings) + 1
  If length < 2 Then Return False
  For i As Integer = LBound(strings) + 1 To UBound(strings)
    If strings(i - 1) >= strings(i) Then Return False
  Next
  Return True

End Function </lang>

Go

For testing for strict ascending order, use sort.StringsAreSorted.


This example is incorrect. Please fix the code and remove this message.

Details: Unless I'm mistaken, StringsAreSorted checks for ascending order (<=), not for strict ascending order (<), i.e. it would return true even if the list contains duplicate strings.

<lang go>if sort.StringsAreSorted([]string{"bar", "foo"})</lang>

Testing for equality needs to be done by hand: <lang Go>func StringsEqual(sl []string) bool { if len(sl) > 1 { for _, s := range sl[1:] { if sl[0] != s { return false } } } return true }</lang> (or by writing a reducer function that takes a function and a list).

Haskell

<lang haskell>allEqual :: String -> Bool allEqual strings = and $ zipWith (==) strings (drop 1 strings)

allIncr :: String -> Bool allIncr strings = and $ zipWith (<) strings (drop 1 strings)</lang>

J

Solution (equality test):<lang j> allEq =: 1 = +/@~: NB. or 1 = #@:~. or -: 1&|. or }.-:}:</lang> Solution (order test):<lang j> asc =: /: -: i.@# NB. or -:&(/:~) etc.</lang> Notes: asc indicates whether y is monotonically increasing, but not necessarily strictly monotonically increasing.

Java

Works with: Java version 8

<lang java5>import java.util.Arrays;

public class CompareListOfStrings {

   public static void main(String[] args) {
       String[][] arr = {{"AA", "AA", "AA", "AA"}, {"AA", "ACB", "BB", "CC"}};
       for (String[] a : arr) {
           System.out.printf("%s%n%s%n%s%n", Arrays.toString(a),
           Arrays.stream(a).distinct().count() < a.length,
           Arrays.equals(Arrays.stream(a).distinct().sorted().toArray(), a));
       }
   }

}</lang>

Output:
[AA, AA, AA, AA]
true
false
[AA, ACB, BB, CC]
false
true

JavaScript

<lang JavaScript>function allEqual(a) {

 var out = true, i = 0;
 while (++i<a.length) {
   out = out && (a[i-1] === a[i]);
 } return out;

}

function azSorted(a) {

 var out = true, i = 0;
 while (++i<a.length) {
   out = out && (a[i-1] < a[i]);
 } return out;

}

var e = ['AA', 'AA', 'AA', 'AA'], s = ['AA', 'ACB', 'BB', 'CC'], empty = [], single = ['AA']; console.log(allEqual(e)); // true console.log(allEqual(s)); // false console.log(allEqual(empty)); // true console.log(allEqual(single)); // true console.log(azSorted(e)); // false console.log(azSorted(s)); // true console.log(azSorted(empty)); // true console.log(azSorted(single)); // true </lang>

jq

Works with: jq version 1.4

For both the following functions, the input is assumed to be a (possibly empty) array of strings. In both cases also, the implementations are fast but could be improved at the expense of complexity. <lang jq># Are the strings all equal? def lexically_equal:

 . as $in
 | reduce range(0;length-1) as $i
     (true; if . then $in[$i] == $in[$i + 1] else false end);
  1. Are the strings in strictly ascending order?

def lexically_ascending:

 . as $in
 | reduce range(0;length-1) as $i
     (true; if . then $in[$i] < $in[$i + 1] else false end);</lang>

Examples: <lang jq>[] | lexically_equal #=> true</lang> <lang jq>["a", "ab"] | lexically_ascending #=> true</lang>

Julia

Julia's built-in comparison functions isequal and isless have no methods for solo arrays, so I solve this task by providing such methods. Empty and single element arrays are defined to be true. These functions are not limited to strings, but a no method matching error will be thrown if the functions encounter any elements that are incompatible under comparison.

Functions <lang Julia> function Base.isequal(a::AbstractArray)

   1 < length(a) || return true
   x = a[1]
   for y in a[2:end]
       x == y || return false
   end
   return true

end

function Base.isless(a::AbstractArray)

   1 < length(a) || return true
   for i in 2:length(a)
       a[i-1] < a[i] || return false
   end
   return true

end </lang>

Here is a slightly shorter, albeit perhaps slower, way to define the two previous functions.

<lang Julia> Base.isequal(l) = all(x-> x == l[1], l)

Base.isless(l) = all(x-> x[1]<x[2], zip(l[1:end-1], l[2:end])) </lang>

Main <lang Julia> tests = {["RC", "RC", "RC"],

        ["RC", "RC", "Rc"],
        ["RA", "RB", "RC"],
        ["RC"],
        ASCIIString[],
        ones(Int64, 4),
        1:4}

for a in tests

   println("\nTesting a = ", a)
   res = isequal(a) ? "are" : "are not"
   println(@sprintf "    The elements of a %s equal." res)
   res = isless(a) ? "are" : "are not"
   println(@sprintf "    The elements of a %s strictly increasing." res)

end println() </lang>

Output:
Testing a = ASCIIString["RC","RC","RC"]
    The elements of a are equal.
    The elements of a are not strictly increasing.

Testing a = ASCIIString["RC","RC","Rc"]
    The elements of a are not equal.
    The elements of a are not strictly increasing.

Testing a = ASCIIString["RA","RB","RC"]
    The elements of a are not equal.
    The elements of a are strictly increasing.

Testing a = ASCIIString["RC"]
    The elements of a are equal.
    The elements of a are strictly increasing.

Testing a = ASCIIString[]
    The elements of a are equal.
    The elements of a are strictly increasing.

Testing a = [1,1,1,1]
    The elements of a are equal.
    The elements of a are not strictly increasing.

Testing a = 1:4
    The elements of a are not equal.
    The elements of a are strictly increasing.

Lua

<lang lua>function identical(t_str)

   _, fst = next(t_str)
   if fst then
       for _, i in pairs(t_str) do
           if i ~= fst then return false end
       end
   end
   return true

end

function ascending(t_str)

   prev = false
   for _, i in ipairs(t_str) do
       if prev and prev >= i then return false end
       prev = i
   end
   return true

end

function check(str)

   t_str = {}
   for i in string.gmatch(str, "[%a_]+") do
       table.insert(t_str, i)
   end
   str = str .. ": "
   if not identical(t_str) then str = str .. "not " end
   str = str .. "identical and "
   if not ascending(t_str) then str = str .. "not " end
   print(str .. "ascending.")

end

check("ayu dab dog gar panda tui yak") check("oy oy oy oy oy oy oy oy oy oy") check("somehow somewhere sometime") check("Hoosiers") check("AA,BB,CC") check("AA,AA,AA") check("AA,CC,BB") check("AA,ACB,BB,CC") check("single_element")</lang>

Output:
ayu dab dog gar panda tui yak: not identical and ascending.
oy oy oy oy oy oy oy oy oy oy: identical and not ascending.
somehow   somewhere  sometim: not identical and not ascending.
Hoosiers: identical and ascending.
AA,BB,CC: not identical and ascending.
AA,AA,AA: identical and not ascending.
AA,CC,BB: not identical and not ascending.
AA,ACB,BB,CC: not identical and ascending.
single_element: identical and ascending.

NetRexx

<lang NetRexx>/* NetRexx */ options replace format comments java crossref symbols nobinary

runSample(arg) return

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ method isEqual(list = Rexx[]) public static binary returns boolean

 state = boolean (1 == 1) -- default to true
 loop ix = 1 while ix < list.length
   state = list[ix - 1] == list[ix]
   if \state then leave ix
   end ix
 return state

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ method isAscending(list = Rexx[]) public static binary returns boolean

 state = boolean (1 == 1) -- default to true
 loop ix = 1 while ix < list.length
   state = list[ix - 1] << list[ix]
   if \state then leave ix
   end ix
 return state

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ method runSample(arg) private static

 samples = [ -
     ['AA', 'BB', 'CC'] -
   , ['AA', 'AA', 'AA'] -
   , ['AA', 'CC', 'BB'] -
   , ['single_element'] -
   ]
 loop ix = 0 while ix < samples.length
   sample = samples[ix]
   if isEqual(sample)     then eq  = 'elements are identical'
                          else eq  = 'elements are not identical'
   if isAscending(sample) then asc = 'elements are in ascending order'
                          else asc = 'elements are not in ascending order'
   say 'List:' Arrays.toString(sample)
   say '  'eq
   say '  'asc
   end ix
 return

</lang>

Output:
List: [AA, BB, CC]
  elements are not identical
  elements are in ascending order
List: [AA, AA, AA]
  elements are identical
  elements are not in ascending order
List: [AA, CC, BB]
  elements are not identical
  elements are not in ascending order
List: [single_element]
  elements are identical
  elements are in ascending order

OCaml

<lang Ocaml> open List;;

let analyze cmp l =

 let rec analyze' l prevs =
   match l with
   [] -> true
   | [s] -> cmp prevs s
   | s::rest -> (cmp prevs s) && (analyze' rest s)
 in analyze' (List.tl l) (List.hd l)

let isEqual = analyze (=) ;; let isAscending = analyze (<) ;;

let test sample =

  List.iter print_endline sample;
  if (isEqual sample)
      then (print_endline "elements are identical")
      else (print_endline "elements are not identical");
  if (isAscending sample)

then print_endline "elements are in ascending order"

        else print_endline "elements are not in ascending order";;


let lasc = ["AA";"BB";"CC";"EE"];; let leq = ["AA";"AA";"AA";"AA"];; let lnoasc = ["AA";"BB";"EE";"CC"];;

List.iter test [lasc;leq;lnoasc];; </lang>

Output:
AA
BB
CC
EE
elements are not identical
elements are in ascending order
AA
AA
AA
AA
elements are identical
elements are not in ascending order
AA
BB
EE
CC
elements are not identical
elements are not in ascending order

Oforth

<lang oforth>: lexEqual asSet size 1 <= ;

lexCmp(l) l l right( l size 1- ) zipWith(#<) and ;</lang>

ooRexx

<lang oorexx>/* REXX ---------------------------------------------------------------

  • 28.06.2014 Walter Pachl
  • --------------------------------------------------------------------*/

Call test 'ABC',.list~of('AA','BB','CC') Call test 'AAA',.list~of('AA','AA','AA') Call test 'ACB',.list~of('AA','CC','BB') Exit

test: Procedure Use Arg name,list all_equal=1 increasing=1 Do i=0 To list~items-2

 i1=i+1
 Select
   When list[i1]==list[i] Then increasing=0
   When list[i1]<<list[i] Then Do
                               all_equal=0
                               increasing=0
                               End
   When list[i1]>>list[i] Then all_equal=0
   End
 End

Select

 When all_equal Then
   Say 'List' name': all elements are equal'
 When increasing Then
   Say 'List' name': elements are in increasing order'
 Otherwise
   Say 'List' name': neither equal nor in increasing order'
 End

Return</lang>

Output:
List ABC: elements are in increasing order
List AAA: all elements are equal
List ACB: neither equal nor in increasing order

PARI/GP

Easiest is to use Set(): <lang parigp>allEqual(strings)=#Set(strings)<2 inOrder(strings)=Set(strings)==strings</lang>

More efficient: <lang parigp>allEqual(strings)=for(i=2,#strings,if(strings[i]!=strings[i-1], return(0))); 1 inOrder(strings)=for(i=2,#strings,if(strings[i]>strings[i-1], return(0))); 1</lang>

Perl

<lang perl>use List::Util 1.33 qw(all);

all { $strings[0] eq $strings[$_] } 1..$#strings # All equal all { $strings[$_-1] lt $strings[$_] } 1..$#strings # Strictly ascending</lang>

Alternatively, if you can guarantee that the input strings don't contain null bytes, the equality test can be performed by a regex like this:

<lang perl>join("\0", @strings) =~ /^ ( [^\0]*+ ) (?: \0 $1 )* $/x # All equal</lang>

Perl 6

In Perl 6, putting square brackets around an infix operator turns it into a listop that effectively works as if the operator had been but in between all of the elements of the argument list (or in technical terms, it folds/reduces the list using that operator, while taking into account the operator's inherent associativity and identity value):

<lang perl6>[eq] @strings # All equal [lt] @strings # Strictly ascending</lang>

Phix

<lang Phix>function allsame(sequence s)

   for i=2 to length(s) do
       if s[i]!=s[1] then return 0 end if
   end for
   return 1

end function

function strict_order(sequence s)

   for i=2 to length(s) do
       if s[i]<=s[i-1] then return 0 end if
   end for
   return 1

end function

procedure test(sequence s)

   ?{s,allsame(s),strict_order(s)}

end procedure

test({"AA","BB","CC"}) test({"AA","AA","AA"}) test({"AA","CC","BB"}) test({"AA","ACB","BB","CC"}) test({"single_element"})</lang>

Output:
{{"AA","BB","CC"},0,1}
{{"AA","AA","AA"},1,0}
{{"AA","CC","BB"},0,0}
{{"AA","ACB","BB","CC"},0,1}
{{"single_element"},1,1}

PicoLisp

PicoLisp has the native operators =, > and < these can take an infinite number of arguments and are also able to compare Transient symbols (the Strings of PicoLisp). <lang PicoLisp>(= "AA" "AA" "AA") -> T (= "AA" "AA" "Aa") -> NIL (< "AA" "AA") -> NIL (< "AA" "Aa") -> T (< "1" "A" "B" "Z" "c" ) -> T (> "A" "B" "Z" "C") -> NIL</lang> If you want a function which takes one list here are some straight-forward implementation: <lang PicoLisp> (de same (List)

 (apply = List))

(de sorted (List)

 (apply < List))

(de sorted-backwards (List)

 (apply > List))

(same '("AA" "AA" "AA")) -> T </lang> This would of course also work with <= and >= without any hassle.

PL/I

<lang pli>*process source xref attributes or(!);

/*--------------------------------------------------------------------
* 01.07.2014 Walter Pachl
*-------------------------------------------------------------------*/
clist: Proc Options(main);
Dcl (hbound) Builtin;
Dcl sysprint Print;
Dcl abc(3) Char(2) Init('AA','BB','CC');
Dcl aaa(3) Char(2) Init('AA','AA','AA');
Dcl acb(3) Char(2) Init('AA','CC','BB');
Call test('ABC',ABC);
Call test('AAA',AAA);
Call test('ACB',ACB);
test: Procedure(name,x);
Dcl name Char(*);
Dcl x(*) Char(*);
Dcl (all_equal,increasing) Bit(1) Init('1'b);
Dcl (i,i1) Bin Fixed(31);
Dcl txt Char(50) Var;
Do i=1 To hbound(x)-1 While(all_equal ! increasing);
 i1=i+1;
 Select;
   When(x(i1)=x(i)) increasing='0'b;
   When(x(i1)<x(i)) Do;
                    increasing='0'b;
                    all_equal='0'b;
                    End;
   Otherwise /* x(i1)>x(i) */
                    all_equal='0'b;
   End;
 End;
 Select;
   When(all_equal)  txt='all elements are equal';
   When(increasing) txt='elements are in increasing order';
   Otherwise        txt='neither equal nor in increasing order';
   End;
 Put Skip List(name!!': '!!txt);
 End;
 End;</lang>
Output:
ABC: elements are in increasing order
AAA: all elements are equal
ACB: neither equal nor in increasing order

PowerShell

Works with: PowerShell version 4.0

<lang PowerShell> function IsAscending ( [string[]]$Array ) { ( 0..( $Array.Count - 2 ) ).Where{ $Array[$_] -le $Array[$_+1] }.Count -eq $Array.Count - 1 } function IsEqual ( [string[]]$Array ) { ( 0..( $Array.Count - 2 ) ).Where{ $Array[$_] -eq $Array[$_+1] }.Count -eq $Array.Count - 1 }

IsAscending 'A', 'B', 'B', 'C' IsAscending 'A', 'C', 'B', 'C' IsAscending 'A', 'A', 'A', 'A'

IsEqual 'A', 'B', 'B', 'C' IsEqual 'A', 'C', 'B', 'C' IsEqual 'A', 'A', 'A', 'A' </lang>

Output:
True
False
True
False
False
True

Python

A useful pattern is that when you need some function of an item in a list with its next item over possibly all items in the list then f(a, nexta) for a, nexta in zip(alist, alist[1:]) works nicely. (Especially if an index is not needed elsewhere in the algorithm). <lang python>all(a == nexta for a, nexta in zip(strings, strings[1:]) # All equal all(a < nexta for a, nexta in zip(strings, strings[1:]) # Strictly ascending</lang>

Racket

Racket mostly has this... see documentation of string=? and string<?.

There are two small issues:

  • Racket will not cope with comparing less than 2 strings
  • also string=? and string<? take variable arguments, so the list has to be applyed to the functions

Hence the wrapper in the code below: <lang racket>#lang racket/base (define ((list-stringX? stringX?) strs)

 (or (null? strs) (null? (cdr strs)) (apply stringX? strs)))

(define list-string=? (list-stringX? string=?)) (define list-string<? (list-stringX? string<?))

(module+ test

 (require tests/eli-tester)
 (test
  (list-string=? '()) => #t
  (list-string=? '("a")) => #t
  (list-string=? '("a" "a")) => #t
  (list-string=? '("a" "a" "a")) => #t
  (list-string=? '("b" "b" "a")) => #f)
 
 (test
  (list-string<? '()) => #t
  (list-string<? '("a")) => #t
  (list-string<? '("a" "b")) => #t
  (list-string<? '("a" "a")) => #f
  (list-string<? '("a" "b" "a")) => #f
  (list-string<? '("a" "b" "c")) => #t))</lang>

REXX

version 1

<lang rexx>/* REXX ---------------------------------------------------------------

  • 28.06.2014 Walter Pachl
  • --------------------------------------------------------------------*/

Call mklist 'ABC','AA','BB','CC' Call test 'ABC' Call mklist 'AAA','AA','AA','AA' Call mklist 'ACB','AA','CC','BB' Call test 'AAA' Call test 'ACB' Exit

mklist:

 list=arg(1)
 do i=1 by 1 To arg()-1
   call value list'.'i,arg(i+1)
   End
 Call value list'.0',i-1
 Return

test: Parse Arg list all_equal=1 increasing=1 Do i=1 To value(list'.0')-1 While all_equal | increasing

 i1=i+1
 Select
   When value(list'.i1')==value(list'.i') Then increasing=0
   When value(list'.i1')<<value(list'.i') Then Do
                                               all_equal=0
                                               increasing=0
                                               End
   When value(list'.i1')>>value(list'.i') Then all_equal=0
   End
 End

Select

 When all_equal Then
   Say 'List' value(list)': all elements are equal'
 When increasing Then
   Say 'List' value(list)': elements are in increasing order'
 Otherwise
   Say 'List' value(list)': neither equal nor in increasing order'
 End

Return</lang>

Output:
List ABC: elements are in increasing order
List AAA: all elements are equal
List ACB: neither equal nor in increasing order

version 2

Programming note:   If a caseless compare (case insensitive) is desired, the two

  • parse arg x

REXX statements could be replaced with either of   (they're equivalent):

  • parse upper arg x
  • arg x

<lang rexx>/*REXX program compares a list of strings for: equality, all ascending. */ string.1= 'ayu dab dog gar panda tui yak' /*seven strings: all are ascending. */ string.2= 'oy oy oy oy oy oy oy oy oy oy' /*ten strings: all are equal. */ string.3= 'somehow somewhere sometime' /*three strings: ¬equal, ¬ascending. */ string.4= 'Hoosiers' /*only a single string defined. */ string.5= /*Null. That is, no strings here. */

          do j=1  for 5;    say;   say          /* [↓]   process the lists of strings. */
          say center(' 'string.j, 50, "═")      /*display a centered title/header.     */
          if ifEqu(string.j)  then  say '  The strings are all equal.'
          if ifAsc(string.j)  then  say '  The strings are ascending.'
          end   /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ ifEqu: procedure; parse arg x /*set X to all the strings in the list.*/

            do k=2  to words(x)                 /*scan the strings in the list.        */
            if word(x, k) \== word(x, k-1)  then return 0  /*is the string ¬= previous?*/
            end   /*k*/                         /* [↑]     0=false,      [↓]   1=true. */
      return 1                                  /*indicate all the strings are equal.  */

/*──────────────────────────────────────────────────────────────────────────────────────*/ ifAsc: procedure; parse arg x /*set X to all the strings in the list.*/

            do k=2  to words(x)                 /*scan the strings in the list.        */
            if word(x, k) <<= word(x, k-1)  then return 0  /*is the string ≤ previous? */
            end   /*k*/                         /*  [↑]    0=false,      [↓]   1=true. */
      return 1                                  /*indicate all strings are ascending.  */</lang>

output   when using the supplied lists:

══════════ ayu dab dog gar panda tui yak══════════
  The strings are ascending.


══════════ oy oy oy oy oy oy oy oy oy oy══════════
  The strings are all equal.


══════════ somehow   somewhere  sometime══════════


════════════════════ Hoosiers═════════════════════
  The strings are all equal.
  The strings are ascending.


════════════════════════ ═════════════════════════
  The strings are all equal.
  The strings are ascending.

version 3

This REXX version is more idiomatic. <lang rexx>/*REXX program compares a list of strings for: equality, all ascending. */ string.1= 'ayu dab dog gar panda tui yak' /*seven strings: all are ascending. */ string.2= 'oy oy oy oy oy oy oy oy oy oy' /*ten strings: all are equal. */ string.3= 'somehow somewhere sometime' /*three strings: ¬equal, ¬ascending. */ string.4= 'Hoosiers' /*only a single string defined. */ string.5= /*Null. That is, no strings here. */

          do j=1  for 5;    say;   say          /* [↓]   process the lists of strings. */
          say center(' 'string.j, 50, "═")      /*display a centered title/header.     */
          if cStr(string.j, 'Equal'    )  then  say  '  The strings are all equal.'
          if cStr(string.j, 'Ascending')  then  say  '  The strings are ascending.'
          end   /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ cStr: procedure; parse arg x; arg , how 2 /*set X to list; get 1st char of arg #2*/

             do k=2  to words(x)                /*scan the strings in the list.        */
             if how=='E' then if word(x,k) \== word(x,k-1)  then return 0 /*¬=previous?*/
             if how=='A' then if word(x,k) <<= word(x,k-1)  then return 0 /*≤ previous?*/
             end   /*k*/                        /* [↓]   1=true.       [↑]   0=false.  */
     return 1                                   /*indicate strings have true comparison*/</lang>

output   is identical to the above REXX version.

Ring

<lang ring> cString1 = "hello" cString2 = "hello" compare(cString1,cString2) cString1 = "abc" cString2 = "bcd" compare(cString1,cString2) cString1 = "bcd" cString2 = "abc" compare(cString1,cString2)

func compare aString, bString

    n = strcmp(aString,bString)
    if n = 0 see aString + " = " + bString + nl
    but n < 0 see aString + " < " + bString + nl
    but n > 0 see aString + " > " + bString + nl ok

</lang>

Ruby

<lang ruby>strings.uniq.one? # all equal? strings == strings.uniq.sort # ascending?</lang>

Short circuiting: <lang ruby>strings.all?{|str| str == strings.first} # all equal? strings.each_cons(2).all?{|str1, str2| str1 < str2} # ascending?</lang>

Sidef

Short-circuiting: <lang ruby>1..arr.end -> all{ arr[0] == arr[_] } # all equal 1..arr.end -> all{ arr[_-1] < arr[_] } # strictly ascending</lang>

Non short-circuiting: <lang ruby>arr.uniq.len == 1 # all equal arr == arr.uniq.sort # strictly ascending</lang>

Tcl

The command form of the eq and < operators (introduced in Tcl 8.5) handle arbitrarily many arguments and will check if they're all equal/ordered. Making the operators work with a list of values is just a matter of using the expansion syntax with them. <lang tcl>tcl::mathop::eq {*}$strings; # All values string-equal tcl::mathop::< {*}$strings; # All values in strict order</lang>

VBScript

<lang vb> Function string_compare(arr) lexical = "Pass" ascending = "Pass" For i = 0 To UBound(arr) If i+1 <= UBound(arr) Then If arr(i) <> arr(i+1) Then lexical = "Fail" End If If arr(i) >= arr(i+1) Then ascending = "Fail" End If End If Next string_compare = "List: " & Join(arr,",") & vbCrLf &_ "Lexical Test: " & lexical & vbCrLf &_ "Ascending Test: " & ascending & vbCrLf End Function

WScript.StdOut.WriteLine string_compare(Array("AA","BB","CC")) WScript.StdOut.WriteLine string_compare(Array("AA","AA","AA")) WScript.StdOut.WriteLine string_compare(Array("AA","CC","BB")) WScript.StdOut.WriteLine string_compare(Array("AA","ACB","BB","CC")) WScript.StdOut.WriteLine string_compare(Array("FF")) </lang>

Output:
List: AA,BB,CC
Lexical Test: Fail
Ascending Test: Pass

List: AA,AA,AA
Lexical Test: Pass
Ascending Test: Fail

List: AA,CC,BB
Lexical Test: Fail
Ascending Test: Fail

List: AA,ACB,BB,CC
Lexical Test: Fail
Ascending Test: Pass

List: FF
Lexical Test: Pass
Ascending Test: Pass

zkl

These short circuit. <lang zkl>fcn allEQ(strings){ (not strings.filter1('!=(strings[0]))) } fcn monoUp(strings){

  strings.len()<2 or
  strings.reduce(fcn(a,b){ if(a>=b) return(Void.Stop,False); b }).toBool() 

}</lang> <lang zkl>allEQ(T("AA")).println(); //True allEQ(T("AA","AA","AA","AA")).println(); //True allEQ(T("A", "AA","AA","AA")).println(); //False

monoUp(T("a")).println(); //True monoUp(T("a","aa","aaa","aaaa")).println(); //True monoUp(T("a","aa","aaa","aaa")).println(); //False monoUp(T("a","b","c","cc")).println(); //True</lang>

ZX Spectrum Basic

Translation of: AWK

<lang zxbasic>10 FOR j=160 TO 200 STEP 10 20 RESTORE j 30 READ n 40 LET test1=1: LET test2=1 50 FOR i=1 TO n 60 READ a$ 70 PRINT a$;" "; 80 IF i=1 THEN GO TO 110 90 IF p$<>a$ THEN LET test1=0 100 IF p$>=a$ THEN LET test2=0 110 LET p$=a$ 120 NEXT i 130 PRINT 'test1'test2 140 NEXT j 150 STOP 160 DATA 3,"AA","BB","CC" 170 DATA 3,"AA","AA","AA" 180 DATA 3,"AA","CC","BB" 190 DATA 4,"AA","ACB","BB","CC" 200 DATA 1,"single_element"</lang>