Determine if a string has all the same characters
- Task
Given a character string (which may be empty, or have a length of zero characters):
- create a function/procedure/routine to:
- determine if all the characters in the string are the same
- indicate if or which character is different from the previous character
- display each string and it's length (as the strings are being examined)
- a zero─length (empty) string shall be considered as all the same character(s)
- process the strings from left─to─right
- if all the same character, display a message saying such
- if not all the same character, then:
- display a message saying such
- display what character is different
- only the 1st different character need be displayed
- display where the different character is in the string
- the above messages can be part of a single message
- display the hexadecimal value of the different character
Use (at least) these seven test values (strings):
- a string of length 0 (an empty string)
- a string of length 3 which contains three blanks
- a string of length 1 which contains: 2
- a string of length 3 which contains: 333
- a string of length 3 which contains: .55
- a string of length 6 which contains: tttTTT
- a string of length 9 which a blank in the middle: 4444 444k
Show all output here on this page.
- Related task
Factor
<lang factor>USING: formatting io kernel math.parser sequences splitting.monotonic ;
- find-diff ( str -- i elt ) dup ?first [ = not ] curry find ;
- len. ( str -- ) dup length "%u — length %d — " printf ;
- same. ( -- ) "contains all the same character." print ;
- diff. ( -- ) "contains a different character at " write ;
- not-same. ( i elt -- )
dup >hex diff. "index %d: '%c' (0x%s)\n" printf ;
- sameness-report. ( str -- )
dup len. find-diff dup [ not-same. ] [ 2drop same. ] if ;
{
"" " " "2" "333" ".55" "tttTTT" "4444 444k"
} [ sameness-report. ] each</lang>
- Output:
"" — length 0 — contains all the same character. " " — length 3 — contains all the same character. "2" — length 1 — contains all the same character. "333" — length 3 — contains all the same character. ".55" — length 3 — contains a different character at index 1: '5' (0x35) "tttTTT" — length 6 — contains a different character at index 3: 'T' (0x54) "4444 444k" — length 9 — contains a different character at index 4: ' ' (0x20)
Go
<lang go>package main
import "fmt"
func analyze(s string) {
// maps each character to a slice of the indices it occurs at charMap := make(map[rune][]int) runes := []rune(s) for i, c := range runes { charMap[c] = append(charMap[c], i) } le := len(runes) fmt.Printf("Analyzing %q which has a length of %d:\n", s, le) if len(charMap) <= 1 { fmt.Println(" All characters in the string are the same.\n") } else { fmt.Println(" The following characters are different:-") for k, v := range charMap { fmt.Printf(" %q (%#x) at indices %v\n", k, k, v) } fmt.Println() }
}
func main() {
strings := []string{ "", " ", "2", "333", ".55", "tttTTT", "4444 444k", "pépé", "🦊🦊🐺🦊", } for _, s := range strings { analyze(s) }
}</lang>
- Output:
Analyzing "" which has a length of 0: All characters in the string are the same. Analyzing " " which has a length of 3: All characters in the string are the same. Analyzing "2" which has a length of 1: All characters in the string are the same. Analyzing "333" which has a length of 3: All characters in the string are the same. Analyzing ".55" which has a length of 3: The following characters are different:- '.' (0x2e) at indices [0] '5' (0x35) at indices [1 2] Analyzing "tttTTT" which has a length of 6: The following characters are different:- 't' (0x74) at indices [0 1 2] 'T' (0x54) at indices [3 4 5] Analyzing "4444 444k" which has a length of 9: The following characters are different:- '4' (0x34) at indices [0 1 2 3 5 6 7] ' ' (0x20) at indices [4] 'k' (0x6b) at indices [8] Analyzing "pépé" which has a length of 4: The following characters are different:- 'p' (0x70) at indices [0 2] 'é' (0xe9) at indices [1 3] Analyzing "🦊🦊🐺🦊" which has a length of 4: The following characters are different:- '🦊' (0x1f98a) at indices [0 1 3] '🐺' (0x1f43a) at indices [2]
Pascal
<lang pascal>program SameNessOfChar; {$IFDEF FPC}
{$MODE DELPHI}{$OPTIMIZATION ON,ALL}{$CODEALIGN proc=16}{$ALIGN 16}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF} uses
sysutils;//Format
const
TestData : array[0..6] of String = (,' ','2','333','.55','tttTTT','4444 444k');
function PosOfDifferentChar(const s: String):NativeInt; var
i: Nativeint; ch:char;
Begin
result := length(s); IF result < 2 then EXIT; ch := s[1]; i := 2; while (i< result) AND (S[i] =ch) do inc(i); result := i;
end;
procedure OutIsAllSame(const s: String); var
l,len: NativeInt;
Begin
l := PosOfDifferentChar(s); len := Length(s); write('"',s,'" of length ',len); IF l = len then writeln(' contains all the same character') else writeln(Format(' is different at position %d "%s" (0x%X)',[l,s[l],Ord(s[l])]));
end;
var
i : NativeInt;
begin
For i := Low(TestData) to HIgh(TestData) do OutIsAllSame(TestData[i]);
end.</lang>
- Output:
"" of length 0 contains all the same character " " of length 3 contains all the same character "2" of length 1 contains all the same character "333" of length 3 contains all the same character ".55" of length 3 is different at position 2 "5" (0x35) "tttTTT" of length 6 is different at position 4 "T" (0x54) "4444 444k" of length 9 is different at position 5 " " (0x20)
Perl 6
The last string demonstrates how Perl 6 can recognize that glyphs made up of different combinations of combining characters can compare the same. It is built up from explicit codepoints to show that each of the glyphs is made up of different combinations.
<lang perl6> -> $str {
my $i = 0; print "\n{$str.perl} (length: {$str.chars}), has "; my %m; %m{$_}.push: ++$i for $str.comb;
if %m > 1 { say "different characters:"; say "'{.key}' ({.key.uninames}; hex ordinal: {(.key.ords).fmt: "0x%X"})" ~ " in positions: {.value.join: ', '}" for %m.sort( *.value[0] ); } else { say "the same character in all positions." }
} for
, ' ', '2', '333', '.55', 'tttTTT', '4444 444k', '🇬🇧🇬🇧🇬🇧🇬🇧', "\c[LATIN CAPITAL LETTER A]\c[COMBINING DIAERESIS]\c[COMBINING MACRON]" ~ "\c[LATIN CAPITAL LETTER A WITH DIAERESIS]\c[COMBINING MACRON]" ~ "\c[LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON]"</lang>
- Output:
"" (length: 0), has the same character in all positions. " " (length: 3), has the same character in all positions. "2" (length: 1), has the same character in all positions. "333" (length: 3), has the same character in all positions. ".55" (length: 3), has different characters: '.' (FULL STOP; hex ordinal: 0x2E) in positions: 1 '5' (DIGIT FIVE; hex ordinal: 0x35) in positions: 2, 3 "tttTTT" (length: 6), has different characters: 't' (LATIN SMALL LETTER T; hex ordinal: 0x74) in positions: 1, 2, 3 'T' (LATIN CAPITAL LETTER T; hex ordinal: 0x54) in positions: 4, 5, 6 "4444 444k" (length: 9), has different characters: '4' (DIGIT FOUR; hex ordinal: 0x34) in positions: 1, 2, 3, 4, 6, 7, 8 ' ' (SPACE; hex ordinal: 0x20) in positions: 5 'k' (LATIN SMALL LETTER K; hex ordinal: 0x6B) in positions: 9 "🇬🇧🇬🇧🇬🇧🇬🇧" (length: 4), has the same character in all positions. "ǞǞǞ" (length: 3), has the same character in all positions.
Python
Functional
<lang python>Determine if a string has all the same characters
from itertools import groupby
- firstDifferingCharLR :: String -> Either String Char
def firstDifferingCharLR(s):
Either a message reporting that no character changes were seen, or the first character (if any) that differs from the character at the head of string. groups = list(groupby(s)) return Right(repr(groups[1][0])) if 1 < len(groups) else ( Left('No character changes') )
- TEST ----------------------------------------------------
- main :: IO ()
def main():
Test of 7 strings for lr in [firstDifferingCharLR(s) for s in [ , ' ', '2', '333', '.55', 'tttTTT', '4444 444' ]]: print(either(lambda l: l)(lambda r: r)(lr))
- GENERIC -------------------------------------------------
- either :: (a -> c) -> (b -> c) -> Either a b -> c
def either(fl):
The application of fl to e if e is a Left value, or the application of fr to e if e is a Right value. return lambda fr: lambda e: fl(e['Left']) if ( None is e['Right'] ) else fr(e['Right'])
- Left :: a -> Either a b
def Left(x):
Constructor for an empty Either (option type) value with an associated string. return {'type': 'Either', 'Right': None, 'Left': x}
- Right :: b -> Either a b
def Right(x):
Constructor for a populated Either (option type) value return {'type': 'Either', 'Left': None, 'Right': x}
- MAIN ---
if __name__ == '__main__':
main()</lang>
- Output:
No character changes No character changes No character changes No character changes '5' 'T' ' '
REXX
<lang rexx>/*REXX program verifies that all characters in a string are all the same (character). */ @chr= ' [character' /* define a literal used for SAY.*/ @all= 'all the same character for string (length' /* " " " " " " */ @.= /*define a default for the @. array. */ parse arg x /*obtain optional argument from the CL.*/ if x\= then @.1= x /*if user specified an arg, use that. */
else do; @.1= /*use this null string if no arg given.*/ @.2= ' ' /* " " " " " " " */ @.3= 2 /* " " " " " " " */ @.4= 333 /* " " " " " " " */ @.5= .55 /* " " " " " " " */ @.6= 'tttTTT' /* " " " " " " " */ @.7= 4444 444k /* " " " " " " " */ end /* [↑] seventh value contains a blank.*/
do j=1; L= length(@.j) /*obtain the length of an array element*/ if j>1 & L==0 then leave /*if arg is null and J>1, then leave. */ r= allSame(@.j) /*R: ≡0, or the location of bad char.*/ if r\==0 then ?= substr(@.j,r,1) /*if not monolithic, obtain the char.*/ if r==0 then say ' ' @all L"):" @.j else say 'not' @all L"):" @.j @chr ? "('"c2x(?)"'x) at position" r"]." end /*j*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ allSame: procedure; parse arg y /*get a value from the argument list. */
if y== then return 0 /*if Y is null, then return 0 (zero)*/ return verify(y, left(y,1) ) /*All chars the same? Return 0 (zero)*/ /* else return location*/</lang>
- output when using the internal default inputs:
all the same character for string (length 0): all the same character for string (length 3): all the same character for string (length 1): 2 all the same character for string (length 3): 333 not all the same character for string (length 3): .55 [character 5 ('35'x) at position 2]. not all the same character for string (length 6): tttTTT [character T ('54'x) at position 4]. not all the same character for string (length 9): 4444 444K [character ('20'x) at position 5].
zkl
<lang zkl>fcn stringSameness(str){ // Does not handle Unicode
sz,unique,uz := str.len(), str.unique(), unique.len(); println("Length %d: \"%s\"".fmt(sz,str)); if(sz==uz or uz==1) println("\tSame character in all positions"); else println("\tDifferent: ", unique.pump(List, // unique[1,*] if don't want first unique character
'wrap(c){ "'%s' (0x%x)[%d]".fmt(c,c.toAsc(),str.find(c)) })
.concat(", "));
}</lang> <lang zkl>testStrings:=T("", " ", "2", "333", ".55", "tttTTT", "4444 444k"); foreach s in (testStrings){ stringSameness(s) }</lang>
- Output:
Length 0: "" Same character in all positions Length 3: " " Same character in all positions Length 1: "2" Same character in all positions Length 3: "333" Same character in all positions Length 3: ".55" Different: '.' (0x2e)[0], '5' (0x35)[1] Length 6: "tttTTT" Different: 't' (0x74)[0], 'T' (0x54)[3] Length 9: "4444 444k" Different: '4' (0x34)[0], ' ' (0x20)[4], 'k' (0x6b)[8]