Compare a list of strings: Difference between revisions

From Rosetta Code
Content added Content deleted
(Add Go)
(Added Ruby)
Line 372: Line 372:
strings are all equal.
strings are all equal.
strings are ascending.
strings are ascending.
</pre>

=={{header|Ruby}}==
<lang ruby>aa = %w(AA AA AA AA)
abc = %w(AA AB AC)
acb = %w(AA CC BB)
[[], ["one"], aa, abc, acb].each do |strings|
puts "List : #{strings}"
puts " %s" % strings.all?{|a| strings[0] == a}
puts " %s" % strings.each_cons(2).all?{|a,b| a<b}
end</lang>

{{out}}
<pre>
List : []
true
true
List : ["one"]
true
true
List : ["AA", "AA", "AA", "AA"]
true
false
List : ["AA", "AB", "AC"]
false
true
List : ["AA", "CC", "BB"]
false
false
</pre>
</pre>



Revision as of 09:35, 10 August 2014

Compare a list of strings is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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

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

Go

For testing for strict ascending order, use sort.StringsAreSorted. <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).

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

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

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

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 to Do What I Mean™):

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

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

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>

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.*/ test.1='ayu dab dog gar panda tui yak' /*seven strings: all ascending. */ test.2='oy oy oy oy oy oy oy oy oy oy' /*ten strings: all equal. */ test.3='somehow somewhere sometime' /*3 strings: ¬equal, ¬assending.*/ test.4='Hoosiers' /*only a single string defined. */ test.5= /*Null. That is, no strings here.*/

        do j=1  for 5;    say;   say  /* [↓]  traipse through the lists*/
        strings=test.j                /*RC task's var name requirement.*/
        say center(' 'strings,50,"═") /*display a centered title/header*/
        if isEqual(strings)   then  say 'strings are all equal.'
        if isAscend(strings)  then  say 'strings are ascending.'
        end   /*j*/

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

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

return 1 /*indicate all strings are equal.*/ /*──────────────────────────────────ISASCEND subroutine─────────────────*/ isAscend: procedure; parse arg x /*set X to the strings in list.*/

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

return 1 /*indicate strings are ascending.*/</lang> output when using the supplied lists:

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


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


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


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


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

Ruby

<lang ruby>aa = %w(AA AA AA AA) abc = %w(AA AB AC) acb = %w(AA CC BB) [[], ["one"], aa, abc, acb].each do |strings|

 puts "List : #{strings}"
 puts "  %s" % strings.all?{|a| strings[0] == a}
 puts "  %s" % strings.each_cons(2).all?{|a,b| a<b}

end</lang>

Output:
List : []
  true
  true
List : ["one"]
  true
  true
List : ["AA", "AA", "AA", "AA"]
  true
  false
List : ["AA", "AB", "AC"]
  false
  true
List : ["AA", "CC", "BB"]
  false
  false

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>