I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Isograms and heterograms

Isograms and heterograms 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.
Definitions

For the purposes of this task, an isogram means a string where each character present is used the same number of times and an n-isogram means an isogram where each character present is used exactly n times.

A heterogram means a string in which no character occurs more than once. It follows that a heterogram is the same thing as a 1-isogram.

Examples

caucasus is a 2-isogram because the letters c, a, u and s all occur twice.

atmospheric is a heterogram because all its letters are used once only.

Using unixdict.txt and ignoring capitalization:

1) Find and display here all words which are n-isograms where n > 1.

Present the results as a single list but sorted as follows:

a. By decreasing order of n;

b. Then by decreasing order of word length;

c. Then by ascending lexicographic order.

2) Secondly, find and display here all words which are heterograms and have more than 10 characters.

Again present the results as a single list but sorted as per b. and c. above.

Reference

## Factor

Works with: Factor version 0.99 2022-04-03
`USING: assocs combinators.short-circuit.smart grouping ioio.encodings.ascii io.files kernel literals math math.ordermath.statistics sequences sets sorting ; CONSTANT: words \$[ "unixdict.txt" ascii file-lines ] : isogram<=> ( a b -- <=> )    { [ histogram values first ] [ length ] } compare-with ; : isogram-sort ( seq -- seq' )    [ isogram<=> invert-comparison ] sort ; : isogram? ( seq -- ? )    histogram values { [ first 1 > ] [ all-eq? ] } && ; : .words-by ( quot -- )    words swap filter isogram-sort [ print ] each ; inline "List of n-isograms where n > 1:" print[ isogram? ] .words-by nl "List of heterograms of length > 10:" print[ { [ length 10 > ] [ all-unique? ] } && ] .words-by`
Output:
```List of n-isograms where n > 1:
aaa
iii
beriberi
bilabial
caucasus
couscous
teammate
appall
emmett
hannah
murmur
tartar
testes
anna
coco
deed
dodo
gogo
isis
juju
lulu
mimi
noon
otto
papa
peep
poop
teet
tete
toot
tutu
ii

List of heterograms of length > 10:
ambidextrous
bluestocking
exclusionary
incomputable
lexicography
loudspeaking
malnourished
atmospheric
blameworthy
centrifugal
christendom
consumptive
countervail
countryside
countrywide
disturbance
documentary
earthmoving
exculpatory
geophysical
inscrutable
misanthrope
problematic
stenography
sulfonamide
switchboard
switzerland
thunderclap
valedictory
voluntarism
```

## J

For this task, we want to know the value of n for n-isograms. This value would be zero for words which are not n-isograms. We can implement this by counting how many times each character occurs and determining whether that value is unique. (If it's the unique value, n is the number of times the first character occurs):

`isogram=: {{ {. (#~ 1= #@~.) #/.~ y }} S:0`

Also, it's worth noting that unixdict.txt is already in sorted order, even after coercing its contents to lower case:

`   (-: /:~) cutLF tolower fread 'unixdict.txt'1`

With this tool and this knowledge, we are ready to tackle this task (the /: expression sorts, and the #~ expression selects):

`   > (/: [email protected],[email protected]#@>) (#~ 1<isogram) cutLF tolower fread 'unixdict.txt'aaa     iii     beriberibilabialcaucasuscouscousteammateappall  emmett  hannah  murmur  tartar  testes  anna    coco    dada    deed    dodo    gogo    isis    juju    lulu    mimi    noon    otto    papa    peep    poop    teet    tete    toot    tutu    ii         > (/: [email protected]#@>) (#~ (10 < #@>) * 1=isogram) cutLF tolower fread 'unixdict.txt'ambidextrousbluestockingexclusionaryincomputablelexicographyloudspeakingmalnourishedatmospheric blameworthy centrifugal christendom consumptive countervail countryside countrywide disturbance documentary earthmoving exculpatory geophysical inscrutable misanthrope problematic selfadjoint stenography sulfonamide switchblade switchboard switzerland thunderclap valedictory voluntarism `

## Julia

`function isogram(word)    wchars, uchars = collect(word), unique(collect(word))    ulen, wlen = length(uchars), length(wchars)    (wlen == 1 || ulen == wlen) && return 1    n = count(==(first(uchars)), wchars)    return all(i -> count(==(uchars[i]), wchars) == n, 2:ulen) ? n : 0end words = split(lowercase(read("documents/julia/unixdict.txt", String)), r"\s+")orderlengthtuples = [(isogram(w), length(w), w) for w in words] tcomp(x, y) = (x[1] != y[1] ? y[1] < x[1] : x[2] != y[2] ? y[2] < x[2] : x[3] < y[3]) nisograms = sort!(filter(t -> t[1] > 1, orderlengthtuples), lt = tcomp)heterograms = sort!(filter(t -> t[1] == 1 && length(t[3]) > 10, orderlengthtuples), lt = tcomp) println("N-Isogram   N  Length\n", "-"^24)foreach(t -> println(rpad(t[3], 8), lpad(t[1], 5), lpad(t[2], 5)), nisograms)println("\nHeterogram   Length\n", "-"^20)foreach(t -> println(rpad(t[3], 12), lpad(t[2], 5)), heterograms) `
Output:
```N-Isogram   N  Length
------------------------
aaa         3    3
iii         3    3
beriberi    2    8
bilabial    2    8
caucasus    2    8
couscous    2    8
teammate    2    8
appall      2    6
emmett      2    6
hannah      2    6
murmur      2    6
tartar      2    6
testes      2    6
anna        2    4
coco        2    4
deed        2    4
dodo        2    4
gogo        2    4
isis        2    4
juju        2    4
lulu        2    4
mimi        2    4
noon        2    4
otto        2    4
papa        2    4
peep        2    4
poop        2    4
teet        2    4
tete        2    4
toot        2    4
tutu        2    4
ii          2    2

Heterogram   Length
--------------------
ambidextrous   12
bluestocking   12
exclusionary   12
incomputable   12
lexicography   12
loudspeaking   12
malnourished   12
atmospheric    11
blameworthy    11
centrifugal    11
christendom    11
consumptive    11
countervail    11
countryside    11
countrywide    11
disturbance    11
documentary    11
earthmoving    11
exculpatory    11
geophysical    11
inscrutable    11
misanthrope    11
problematic    11
stenography    11
sulfonamide    11
switchboard    11
switzerland    11
thunderclap    11
valedictory    11
voluntarism    11
```

## Perl

`use strict;use warnings;use feature 'say';use Path::Tiny;use List::Util 'uniq'; my @words = map { lc } path('unixdict.txt')->slurp =~ /^[A-z]{2,}\$/gm; my(@heterogram, %isogram);for my \$w (@words) {    my %l;    \$l{\$_}++ for split '', \$w;    next unless 1 == scalar (my @x = uniq values %l);    if (\$x[0] == 1) { push @heterogram,        \$w if length \$w > 10 }    else            { push @{\$isogram{\$x[0]}}, \$w                   }} for my \$n (reverse sort keys %isogram) {    my @i = sort { length \$b <=> length \$a } @{\$isogram{\$n}};    say scalar @i . " \$n-isograms:\n" . join("\n", @i) . "\n";} say scalar(@heterogram) . " heterograms with more than 10 characters:\n" . join "\n", sort { length \$b <=> length \$a } @heterogram;`
Output:
```2 3-isograms:
aaa
iii

31 2-isograms:
beriberi
bilabial
caucasus
couscous
teammate
appall
emmett
hannah
murmur
tartar
testes
anna
coco
deed
dodo
gogo
isis
juju
lulu
mimi
noon
otto
papa
peep
poop
teet
tete
toot
tutu
ii

32 heterograms with more than 10 characters:
ambidextrous
bluestocking
exclusionary
incomputable
lexicography
loudspeaking
malnourished
atmospheric
blameworthy
centrifugal
christendom
consumptive
countervail
countryside
countrywide
disturbance
documentary
earthmoving
exculpatory
geophysical
inscrutable
misanthrope
problematic
stenography
sulfonamide
switchboard
switzerland
thunderclap
valedictory
voluntarism
```

## Phix

```with javascript_semantics
function isogram(string word)
sequence chars = {}, counts = {}
for ch in word do
integer k = find(ch,chars)
if k=0 then
chars &= ch
counts &= 1
else
counts[k] += 1
end if
end for
integer c1 = counts[1], lc = length(counts), lw = length(word)
return iff((c1>1 or lw>10) and counts=repeat(c1,lc)?{word,c1,lw}:0)
end function

sequence res = sort_columns(filter(apply(unix_dict(),isogram),"!=",0),{-2,-3,1})
printf(1,"word           n length\n%s\n",{join(res,'\n',fmt:="%-14s %d %6d")})
```
Output:
```word           n length
aaa            3      3
iii            3      3
beriberi       2      8
bilabial       2      8
caucasus       2      8
couscous       2      8
teammate       2      8
appall         2      6
emmett         2      6
hannah         2      6
murmur         2      6
tartar         2      6
testes         2      6
anna           2      4
coco           2      4
deed           2      4
dodo           2      4
gogo           2      4
isis           2      4
juju           2      4
lulu           2      4
mimi           2      4
noon           2      4
otto           2      4
papa           2      4
peep           2      4
poop           2      4
teet           2      4
tete           2      4
toot           2      4
tutu           2      4
ii             2      2
ambidextrous   1     12
bluestocking   1     12
exclusionary   1     12
incomputable   1     12
lexicography   1     12
loudspeaking   1     12
malnourished   1     12
atmospheric    1     11
blameworthy    1     11
centrifugal    1     11
christendom    1     11
consumptive    1     11
countervail    1     11
countryside    1     11
countrywide    1     11
disturbance    1     11
documentary    1     11
earthmoving    1     11
exculpatory    1     11
geophysical    1     11
inscrutable    1     11
misanthrope    1     11
problematic    1     11
stenography    1     11
sulfonamide    1     11
switchboard    1     11
switzerland    1     11
thunderclap    1     11
valedictory    1     11
voluntarism    1     11
```

## Raku

`my \$file = 'unixdict.txt'; my @words = \$file.IO.slurp.words.race.map: { \$_ => .comb.Bag }; .say for (6...2).map: -> \$n {    next unless my @iso = @words.race.grep({.value.values.all == \$n})».key;    "\n({[email protected]}) {\$n}-isograms:\n" ~ @iso.sort({[-.chars, ~\$_]}).join: "\n";} my \$minchars = 10; say "\n({+\$_}) heterograms with more than \$minchars characters:\n" ~  .sort({[-.chars, ~\$_]}).join: "\n" given  @words.race.grep({.key.chars >\$minchars && .value.values.max == 1})».key;`
Output:
```(2) 3-isograms:
aaa
iii

(31) 2-isograms:
beriberi
bilabial
caucasus
couscous
teammate
appall
emmett
hannah
murmur
tartar
testes
anna
coco
deed
dodo
gogo
isis
juju
lulu
mimi
noon
otto
papa
peep
poop
teet
tete
toot
tutu
ii

(32) heterograms with more than 10 characters:
ambidextrous
bluestocking
exclusionary
incomputable
lexicography
loudspeaking
malnourished
atmospheric
blameworthy
centrifugal
christendom
consumptive
countervail
countryside
countrywide
disturbance
documentary
earthmoving
exculpatory
geophysical
inscrutable
misanthrope
problematic
stenography
sulfonamide
switchboard
switzerland
thunderclap
valedictory
voluntarism```

## Wren

Library: Wren-str
`import "io" for Fileimport "./str" for Str var isogram = Fn.new { |word|    if (word.count == 1) return 1    var map = {}    word = Str.lower(word)    for (c in word) {        if (map.containsKey(c)) {            map[c] = map[c] + 1        } else {            map[c] = 1        }    }    var chars = map.keys.toList    var n = map[chars[0]]    var iso = chars[1..-1].all { |c| map[c] == n }    return iso ? n : 0} var isoComparer = Fn.new { |i, j|    if (i[1] != j[1]) return i[1] > j[1]    if (i[0].count != j[0].count) return i[0].count > j[0].count    return Str.le(i[0], j[0])} var heteroComparer = Fn.new { |i, j|    if (i[0].count != j[0].count) return i[0].count > j[0].count    return Str.le(i[0], j[0])} var wordList = "unixdict.txt" // local copyvar words = File.read(wordList)                .trimEnd()                .split("\n")                .map { |word| [word, isogram.call(word)] } var isograms = words.where { |t| t[1] > 1 }                    .toList                    .sort(isoComparer)                    .map { |t| "  " + t[0] }                    .toListSystem.print("List of n-isograms(%(isograms.count)) where n > 1:")System.print(isograms.join("\n")) var heterograms = words.where { |t| t[1] == 1 && t[0].count > 10 }                       .toList                       .sort(heteroComparer)                       .map { |t| "  " + t[0] }                       .toListSystem.print("\nList of heterograms(%(heterograms.count)) of length > 10:")System.print(heterograms.join("\n"))`
Output:
```List of n-isograms(33) where n > 1:
aaa
iii
beriberi
bilabial
caucasus
couscous
teammate
appall
emmett
hannah
murmur
tartar
testes
anna
coco
deed
dodo
gogo
isis
juju
lulu
mimi
noon
otto
papa
peep
poop
teet
tete
toot
tutu
ii

List of heterograms(32) of length > 10:
ambidextrous
bluestocking
exclusionary
incomputable
lexicography
loudspeaking
malnourished
atmospheric
blameworthy
centrifugal
christendom
consumptive
countervail
countryside
countrywide
disturbance
documentary
earthmoving
exculpatory
geophysical
inscrutable
misanthrope
problematic