Hex words

From Rosetta Code
Hex words 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.
Definition

For the purposes of this task a hex word means a word which (in lower case form) consists entirely of the letters a, b, c, d, e and f.

Task

Using unixdict.txt, find all hex words with 4 letters or more.

Convert each such word to its decimal equivalent and compute its base 10 digital root.

Display all three in increasing order of digital root and show the total count of such words.

Keeping only words which contain at least 4 distinct letters, display the same statistics but in decreasing order of decimal equivalent together with their total count.

11l[edit]

Translation of: Python
F digroot(=n)
   L n > 9
      n = sum(String(n).map(d -> Int(d)))
   R n

V lines = File(‘unixdict.txt’).read().split("\n")
V words = lines.filter(w -> w.len >= 4 & all(w.map(c -> c C ‘abcdef’)))
V results = words.map(w -> (w, Int(w, radix' 16), digroot(Int(w, radix' 16))))

print("Hex words in unixdict.txt:\nRoot  Word      Base 10\n "(‘-’ * 22))
L(a) sorted(results, key' x -> x[2])
   print(f:‘{a[2]}     {a[0]:<6}{a[1]:10}’)

print(‘Total count of these words: ’results.len)
print("\nHex words with > 3 distinct letters:\nRoot  Word      Base 10\n "(‘-’ * 22))
results = results.filter(a -> Set(Array(String(a[0]))).len > 3)
L(a) sorted(results, key' x -> x[2])
   print(f:‘{a[2]}     {a[0]:<6}{a[1]:10}’)

print(‘Total count of those words: ’results.len)
Output:
Hex words in unixdict.txt:
Root  Word      Base 10
 ----------------------
1     ababa     703162
1     abbe       43966
1     dada       56026
1     deaf       57007
1     decade  14600926
2     cede       52958
2     feed       65261
3     abed       44013
3     added     712173
3     bade       47838
4     beebe     782014
4     decca     912586
5     dade       56030
6     bead       48813
6     deface  14613198
7     babe       47806
7     fade       64222
8     dead       57005
8     efface  15727310
8     facade  16435934
9     accede  11325150
9     beef       48879
9     cafe       51966
9     dacca     896202
9     deed       57069
9     face       64206
Total count of these words: 26

Hex words with > 3 distinct letters:
Root  Word      Base 10
 ----------------------
1     deaf       57007
1     decade  14600926
3     abed       44013
3     bade       47838
4     decca     912586
6     bead       48813
6     deface  14613198
7     fade       64222
8     efface  15727310
8     facade  16435934
9     accede  11325150
9     cafe       51966
9     face       64206
Total count of those words: 13

ALGOL 68[edit]

# find words that contain only hex digits a-f #
IF  FILE input file;
    STRING file name = "unixdict.txt";
    open( input file, file name, stand in channel ) /= 0
THEN
    # failed to open the file #
    print( ( "Unable to open """ + file name + """", newline ) )
ELSE
    # file opened OK #
    BOOL at eof := FALSE;
    # set the EOF handler for the file #
    on logical file end( input file, ( REF FILE f )BOOL:
                                     BEGIN
                                         # note that we reached EOF on the #
                                         # latest read #
                                         at eof := TRUE;
                                         # return TRUE so processing can continue #
                                         TRUE
                                     END
                       );
    INT count := 0;
    INT max words = 100; # guess at the maximum number of words #
    MODE HEXWORD = STRUCT( STRING word, LONG INT value, INT root, INT len );
    [ 1 : max words ]HEXWORD hw;
    WHILE STRING word;
          get( input file, ( word, newline ) );
          NOT at eof
    DO
        # check the word contains only a-f and compute its decimal value #
        IF  INT word len = ( UPB word + 1 ) - LWB word;
            word len >= 4
        THEN
            # the word is at least 4 characters long #
            BOOL is hex word := word /= "";
            LONG INT  int word    := 0;
            FOR i FROM LWB word TO UPB word
            WHILE is hex word := word[ i ] >= "a" AND word[ i ] <= "f"
            DO
                int word *:= 16;
                int word +:= ( ABS word[ i ] - ABS "a" ) + 10
            OD;
            IF is hex word
            THEN
                # have a hex word #
                count +:= 1;
                # compute the digital root #
                LONG INT r := int word;
                WHILE r > 9 DO
                    LONG INT dr := r MOD 10;
                    WHILE ( r OVERAB 10 ) > 0 DO dr +:= r MOD 10 OD;
                    r := dr
                OD;
                word  OF hw[ count ] := word;
                value OF hw[ count ] := int word;
                root  OF hw[ count ] := SHORTEN r;
                len   OF hw[ count ] := word len
            FI
        FI
    OD;
    close( input file );
    # prints the HEXWORD hw #
    PROC show = ( HEXWORD hw )VOID:
    BEGIN
        STRING pad = IF len OF hw >= 12 THEN "" ELSE ( 12 - len OF hw ) * " " FI;
        print( ( word OF hw, ": ", pad, whole( value OF hw, -10 ), " [ ", whole( root OF hw, 0 ), " ]", newline ) )
    END # show # ;
    # Quicksorts in-place the array of HEXWORDS a, from lb to ub on ascending value #
    PROC quicksort = ( REF[]HEXWORD a, INT lb, ub )VOID:
         IF ub > lb THEN
            # more than one element, so must sort #
            INT left   := lb;
            INT right  := ub;
            # choosing the middle element of the array as the pivot #
            LONG INT pivot  := value OF a[ left + ( ( right + 1 ) - left ) OVER 2 ];
            WHILE
                WHILE IF left  <= ub THEN value OF a[ left  ] < pivot ELSE FALSE FI DO left  +:= 1 OD;
                WHILE IF right >= lb THEN value OF a[ right ] > pivot ELSE FALSE FI DO right -:= 1 OD;
                left <= right
            DO
                HEXWORD t  := a[ left  ];
                a[ left  ] := a[ right ];
                a[ right ] := t;
                left      +:= 1;
                right     -:= 1
            OD;
            quicksort( a, lb,   right );
            quicksort( a, left, ub    )
         FI # quicksort # ;
    # show the hex words in ascending order of digital root #
    FOR r FROM 1 TO 9 DO
        FOR i FROM 1 TO count DO
            IF root OF hw[ i ] = r THEN show( hw[ i ] ) FI
        OD
    OD;
    print( ( "Found ", whole( count, 0 ), " hex words", newline, newline ) );
    # show the words in descending value order excluding those with less than 4 unique letters #
    quicksort( hw, 1, count );
    INT count 4 := 0;
    FOR i FROM count BY -1 TO 1 DO
        # check the word has at least four different digits #
        INT a := 0, b := 0, c := 0, d := 0, e := 0, f := 0;
        FOR c pos FROM LWB word OF hw[ i ] TO UPB word OF hw[ i ] DO
            IF   CHAR ch = ( word OF hw[ i ] )[ c pos ];
                 ch = "a"
            THEN a := 1
            ELIF ch = "b"
            THEN b := 1
            ELIF ch = "c"
            THEN c := 1
            ELIF ch = "d"
            THEN d := 1
            ELIF ch = "e"
            THEN e := 1
            ELSE f := 1
            FI
        OD;
        IF a + b + c + d + e + f >= 4
        THEN
            # have a hex word with at least 4 different digits #
            count 4 +:= 1;
            show( hw[ i ] )
        FI
    OD;
    print( ( "Found ", whole( count 4, 0 ), " hex words with 4 or more distinct digits", newline ) )
FI
Output:
ababa:            703162 [ 1 ]
abbe:              43966 [ 1 ]
dada:              56026 [ 1 ]
deaf:              57007 [ 1 ]
decade:         14600926 [ 1 ]
cede:              52958 [ 2 ]
feed:              65261 [ 2 ]
abed:              44013 [ 3 ]
added:            712173 [ 3 ]
bade:              47838 [ 3 ]
beebe:            782014 [ 4 ]
decca:            912586 [ 4 ]
dade:              56030 [ 5 ]
bead:              48813 [ 6 ]
deface:         14613198 [ 6 ]
babe:              47806 [ 7 ]
fade:              64222 [ 7 ]
dead:              57005 [ 8 ]
efface:         15727310 [ 8 ]
facade:         16435934 [ 8 ]
accede:         11325150 [ 9 ]
beef:              48879 [ 9 ]
cafe:              51966 [ 9 ]
dacca:            896202 [ 9 ]
deed:              57069 [ 9 ]
face:              64206 [ 9 ]
Found 26 hex words

facade:         16435934 [ 8 ]
efface:         15727310 [ 8 ]
deface:         14613198 [ 6 ]
decade:         14600926 [ 1 ]
accede:         11325150 [ 9 ]
decca:            912586 [ 4 ]
fade:              64222 [ 7 ]
face:              64206 [ 9 ]
deaf:              57007 [ 1 ]
cafe:              51966 [ 9 ]
bead:              48813 [ 6 ]
bade:              47838 [ 3 ]
abed:              44013 [ 3 ]
Found 13 hex words with 4 or more distinct digits

AppleScript[edit]

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
use scripting additions
use sorter : script "Custom Iterative Ternary Merge Sort" -- <https://macscripter.net/viewtopic.php?pid=194430#p194430>

on hexToInt(hex)
    set digits to "0123456789ABCDEF"
    set n to 0
    set astid to AppleScript's text item delimiters
    ignoring case
        repeat with thisDigit in hex
            set AppleScript's text item delimiters to thisDigit
            set n to n * 16 + (count digits's first text item)
        end repeat
    end ignoring
    set AppleScript's text item delimiters to astid
    return n
end hexToInt

on digitalRoot(r, base)
    repeat until (r < base)
        set n to r
        set r to 0
        repeat until (n = 0)
            set r to r + n mod base
            set n to n div base
        end repeat
    end repeat
    return r as integer
end digitalRoot

on join(lst, delim)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delim
    set txt to lst as text
    set AppleScript's text item delimiters to astid
    return txt
end join

on task()
    set unixdictPath to (path to desktop as text) & "unixdict.txt" -- HFS path. Edit as required.
    set allWords to (read (unixdictPath as «class furl») from 1 as «class utf8»)'s words
    set hexwordFilter to current application's class "NSPredicate"'s
        predicateWithFormat:("self MATCHES '^[a-f]{4,}+$'")
    set hexwords to ((current application's class "NSArray"'s arrayWithArray:(allWords))'s ¬
        filteredArrayUsingPredicate:(hexwordFilter))
    
    set {list1, list2} to {{}, {}}
    repeat with thisWord in hexwords
        set thisWord to thisWord as text
        set decimal to hexToInt(thisWord) -- Actually an AppleScript integer.
        set root to digitalRoot(decimal, 10) -- Ditto.
        set thisEntry to {thisWord, decimal, root}
        set end of list1 to thisEntry
        set distinctChars to (current application's class "NSSet"'s setWithArray:(thisWord's characters))
        if (distinctChars's |count|() > 3) then set end of list2 to thisEntry
    end repeat
    
    -- Sort list1 on its sublists' digital root values.
    script
        on isGreater(a, b)
            return (a's end > b's end)
        end isGreater
    end script
    tell sorter to sort(list1, 1, -1, {comparer:result})
    -- Reverse sort list2 on its sublists' decimal equivalent values.
    script
        on isGreater(a, b)
            return (a's 2nd item < b's 2nd item)
        end isGreater
    end script
    tell sorter to sort(list2, 1, -1, {comparer:result})
    
    -- Format for display and output.
    set padding to "          "
    repeat with thisList in {list1, list2}
        repeat with thisEntry in thisList
            tell thisEntry to set its contents to text 1 thru 9 of (its beginning & padding) & ¬
                text -9 thru -1 of (padding & its 2nd item) & text -9 thru -1 of (padding & its end)
        end repeat
        set thisList's beginning to "
Word       Decimal      Root
----------------------------"
    end repeat
    set beginning of list1 to linefeed & ((count list1) - 1) & ¬
        " 4+-character hexwords, sorted on their decimal equivalents' digital roots:"
    set beginning of list2 to linefeed & "The " & ((count list2) - 1) & ¬
        " with at least 4 /different/ characters, reverse sorted on their decimal equivalents:"
    
    return join({list1, list2}, linefeed)
end task

task()
Output:
"
26 4+-character hexwords, sorted on their decimal equivalents' digital roots:

Word       Decimal      Root
----------------------------
ababa       703162        1
abbe         43966        1
dada         56026        1
deaf         57007        1
decade    14600926        1
cede         52958        2
feed         65261        2
abed         44013        3
added       712173        3
bade         47838        3
beebe       782014        4
decca       912586        4
dade         56030        5
bead         48813        6
deface    14613198        6
babe         47806        7
fade         64222        7
dead         57005        8
efface    15727310        8
facade    16435934        8
accede    11325150        9
beef         48879        9
cafe         51966        9
dacca       896202        9
deed         57069        9
face         64206        9

The 13 with at least 4 /different/ characters, reverse sorted on their decimal equivalents:

Word       Decimal      Root
----------------------------
facade    16435934        8
efface    15727310        8
deface    14613198        6
decade    14600926        1
accede    11325150        9
decca       912586        4
fade         64222        7
face         64206        9
deaf         57007        1
cafe         51966        9
bead         48813        6
bade         47838        3
abed         44013        3"

Arturo[edit]

words: map read.lines relative "unixdict.txt" => strip
hexWords: new []

digRoot: function [num][
    res: num
    while [1 < size digits res][
        res: sum digits res
    ]
    return res
]

printTable: function [wrds][
    print [pad.center "Root" 10 pad.right "Word" 10 pad "Base-10" 15]
    print repeat "-" 38
    loop wrds 'wrd [
        print [pad.center to :string wrd\root 10 pad.right wrd\hex 10 pad to :string wrd\decimal 15]
    ]
    print repeat "-" 38
    print ["   Total count:" size wrds "\n"]
]

loop words 'word [
    if 4 > size word ->
        continue

    if not? empty? match word {/^[a-f]+$/} [
        base10: from.hex word
        droot: digRoot base10
        'hexWords ++ #[
            root: droot
            hex: word
            decimal: base10
        ]
    ]
]

printTable sort.by:'root hexWords
printTable sort.descending.by:'decimal select hexWords 'h ->
    4 =< size unique split h\hex
Output:
   Root    Word               Base-10 
--------------------------------------
    1      ababa               703162 
    1      abbe                 43966 
    1      dada                 56026 
    1      deaf                 57007 
    1      decade            14600926 
    2      cede                 52958 
    2      feed                 65261 
    3      abed                 44013 
    3      added               712173 
    3      bade                 47838 
    4      beebe               782014 
    4      decca               912586 
    5      dade                 56030 
    6      bead                 48813 
    6      deface            14613198 
    7      babe                 47806 
    7      fade                 64222 
    8      dead                 57005 
    8      efface            15727310 
    8      facade            16435934 
    9      accede            11325150 
    9      beef                 48879 
    9      cafe                 51966 
    9      dacca               896202 
    9      deed                 57069 
    9      face                 64206 
--------------------------------------
   Total count: 26 
 
   Root    Word               Base-10 
--------------------------------------
    8      facade            16435934 
    8      efface            15727310 
    6      deface            14613198 
    1      decade            14600926 
    9      accede            11325150 
    4      decca               912586 
    7      fade                 64222 
    9      face                 64206 
    1      deaf                 57007 
    9      cafe                 51966 
    6      bead                 48813 
    3      bade                 47838 
    3      abed                 44013 
--------------------------------------
   Total count: 13

AutoHotkey[edit]

FileRead, wList, % A_Desktop "\unixdict.txt"
hexWords := Hex_words(wList)
Header := "Base 10`t`tWord`tRoot`n"
for dr, obj in hexWords
    for word, dec in obj
        result .= dec "`t" (StrLen(dec) < 8 ? "`t" : "") word "`t" dr "`n"

MsgBox, 262144, ,% result := Header . result . "`n4 distinct letter words:`n" . Header . filter(result, "abcdef", 4)
return
;-------------------------------------------
filter(result, letters, Count){
    for i, line in StrSplit(result, "`n", "`r") {
        counter := 0
        for j, letter in StrSplit(letters)
            StrReplace(line, letter, letter, cnt, 1), counter += cnt
        if (counter >= Count)
            filtered .= line "`n"
    }
    Sort, filtered, RN
    return filtered
}
;-------------------------------------------
Hex_words(wList){
    hexWords := []
    for i, w in StrSplit(wList, "`n", "`r") {
        if (StrLen(w) < 4 || w ~= "i)[^abcdef]")
            continue
        dec := hex2dec(w)
        dr := digital_root(dec)
        hexWords[dr, w] := dec
    }
    return hexWords
}
;-------------------------------------------
digital_root(n){
    loop {
        sum := 0, i := 1
        while (i <= StrLen(n))
            sum += SubStr(n, i++, 1)
        n := sum
    }
    until (sum < 10)
    return sum
}
Output:
Base 10		Word	Root
703162		ababa	1
43966		abbe	1
56026		dada	1
57007		deaf	1
14600926	decade	1
52958		cede	2
65261		feed	2
44013		abed	3
712173		added	3
47838		bade	3
782014		beebe	4
912586		decca	4
56030		dade	5
48813		bead	6
14613198	deface	6
47806		babe	7
64222		fade	7
57005		dead	8
15727310	efface	8
16435934	facade	8
11325150	accede	9
48879		beef	9
51966		cafe	9
896202		dacca	9
57069		deed	9
64206		face	9

4 distinct letter words:
Base 10		Word	Root
16435934	facade	8
15727310	efface	8
14613198	deface	6
14600926	decade	1
11325150	accede	9
912586		decca	4
64222		fade	7
64206		face	9
57007		deaf	1
51966		cafe	9
48813		bead	6
47838		bade	3
44013		abed	3

AWK[edit]

# syntax: GAWK -f HEX_WORDS.AWK unixdict.txt
{   nf += NF
    if (length($1) >= 4) {
      if ($0 ~ /^[a-fA-F]{4,}$/) {
        base10 = hex2dec($1)
        dr = digital_root(base10)
        arr[dr " " $1] = base10
      }
    }
}
ENDFILE {
    printf("%s: %d records, %d fields\n\n",FILENAME,FNR,nf)
}
END {
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (i in arr) {
      printf("%-8s %10d \n",i,arr[i])
      count1++
    }
    printf("Found %d hex words\n\n",count1)
    PROCINFO["sorted_in"] = "@val_num_desc"
    for (i in arr) {
      if (distinct(substr(i,3)) >= 4) {
        printf("%-8s %10d \n",i,arr[i])
        count2++
      }
    }
    printf("Found %d hex words with 4 or more distinct\n\n",count2)
    exit(0)
}
function digital_root(n,  i,sum) {
    while (1) {
      sum = 0
      for (i=1; i<=length(n); i++) {
        sum += substr(n,i,1)
      }
      if (sum < 10) {
        break
      }
      n = sum
    }
    return(sum)
}
function distinct(str,  arr,i) {
    for (i=1; i<=length(str); i++) {
      arr[substr(str,i,1)]++
    }
    return(length(arr))
}
function hex2dec(s,  num) {
    num = index("0123456789ABCDEF",toupper(substr(s,length(s)))) - 1
    sub(/.$/,"",s)
    return num + (length(s) ? 16*hex2dec(s) : 0)
}
Output:
unixdict.txt: 25104 records, 25104 fields

1 ababa      703162
1 abbe        43966
1 dada        56026
1 deaf        57007
1 decade   14600926
2 cede        52958
2 feed        65261
3 abed        44013
3 added      712173
3 bade        47838
4 beebe      782014
4 decca      912586
5 dade        56030
6 bead        48813
6 deface   14613198
7 babe        47806
7 fade        64222
8 dead        57005
8 efface   15727310
8 facade   16435934
9 accede   11325150
9 beef        48879
9 cafe        51966
9 dacca      896202
9 deed        57069
9 face        64206
Found 26 hex words

8 facade   16435934
8 efface   15727310
6 deface   14613198
1 decade   14600926
9 accede   11325150
4 decca      912586
7 fade        64222
9 face        64206
1 deaf        57007
9 cafe        51966
6 bead        48813
3 bade        47838
3 abed        44013
Found 13 hex words with 4 or more distinct

Factor[edit]

Works with: Factor version 0.99 2021-06-02
USING: formatting io io.encodings.ascii io.files kernel literals
math math.parser prettyprint sequences sets sorting ;

CONSTANT: words $[
    "unixdict.txt" ascii file-lines
    [ length 3 > ] filter
    [ "abcdef" subset? ] filter
]

: droot ( m -- n ) 1 - 9 mod 1 + ;

: info. ( str -- ) dup hex> dup droot "%-8s-> %-10d-> %d\n" printf ;

: info-by ( quot ? -- )
    [ sort-with ] [ inv-sort-with ] if [ length ] keep
    [ info. ] each pprint ; inline

words [ hex> droot ] t info-by
" hex words with 4 or more letters found." print nl

words [ cardinality 3 > ] filter [ hex> ] f info-by
" such words found which contain 4 or more different digits." print
Output:
ababa   -> 703162    -> 1
abbe    -> 43966     -> 1
dada    -> 56026     -> 1
deaf    -> 57007     -> 1
decade  -> 14600926  -> 1
cede    -> 52958     -> 2
feed    -> 65261     -> 2
abed    -> 44013     -> 3
added   -> 712173    -> 3
bade    -> 47838     -> 3
beebe   -> 782014    -> 4
decca   -> 912586    -> 4
dade    -> 56030     -> 5
bead    -> 48813     -> 6
deface  -> 14613198  -> 6
babe    -> 47806     -> 7
fade    -> 64222     -> 7
dead    -> 57005     -> 8
efface  -> 15727310  -> 8
facade  -> 16435934  -> 8
accede  -> 11325150  -> 9
beef    -> 48879     -> 9
cafe    -> 51966     -> 9
dacca   -> 896202    -> 9
deed    -> 57069     -> 9
face    -> 64206     -> 9
26 hex words with 4 or more letters found.

facade  -> 16435934  -> 8
efface  -> 15727310  -> 8
deface  -> 14613198  -> 6
decade  -> 14600926  -> 1
accede  -> 11325150  -> 9
decca   -> 912586    -> 4
fade    -> 64222     -> 7
face    -> 64206     -> 9
deaf    -> 57007     -> 1
cafe    -> 51966     -> 9
bead    -> 48813     -> 6
bade    -> 47838     -> 3
abed    -> 44013     -> 3
13 such words found which contain 4 or more different digits.

J[edit]

   (#~$&1 0@#)(#,&":])/:~((+/@(".&>@":)^:_@]; ;~) dfh)@> (#~ (*/@e.&'abcdef' * 3<#)@>) cutLF fread'unixdict.txt'
26                 
143966   abbe  
156026   dada  
157007   deaf  
1703162  ababa 
114600926decade
252958   cede  
265261   feed  
344013   abed  
347838   bade  
3712173  added 
4782014  beebe 
4912586  decca 
556030   dade  
648813   bead  
614613198deface
747806   babe  
764222   fade  
857005   dead  
815727310efface
816435934facade
948879   beef  
951966   cafe  
957069   deed  
964206   face  
9896202  dacca 
911325150accede
   (#~ $&1 0@#)(#,&":])\:~((+/@(".&>@":)^:_@]; ;~) dfh)@> (#~ (*/@e.&'abcdef' * 3 < #@~.)@>) cutLF fread'unixdict.txt'
13                 
911325150accede
964206   face  
951966   cafe  
816435934facade
815727310efface
764222   fade  
614613198deface
648813   bead  
4912586  decca 
347838   bade  
344013   abed  
114600926decade
157007   deaf  

jq[edit]

Works with: jq

Works with gojq, the Go implementation of jq

Preliminaries

def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;

# . may be a decimal number or a string representing a decimal number
def digital_root:
  # string-only version
  def dr:
    # state: [mdr, persist]
    until( .[0] | length == 1;
              [ (.[0] | explode | map(.-48) | add | tostring), .[1] + 1 ]
              );
  [tostring, 0] | dr | .[0] | tonumber;

# lowercase a-f
def isHexWord:
  all(explode[]; 97 <= . and . <= 102);

# Input: a valid hex number (all lowercase)
def hex2i:
  def toi: if . >= 87 then .-87 else . - 48 end;
  reduce (explode | map(toi) | reverse[]) as $i ([1, 0]; # [power, sum]
    .[1] += $i * .[0]
    | .[0] *= 16 )
  | .[1];

The Task

def task:

  def format: "\(.[0]|lpad(8)) -> \(.[1]|lpad(9)) -> \(.[2])";
  
  INDEX(inputs | select(length>=4 and isHexWord); .)
  | reduce keys_unsorted[] as $word ([];
        ($word | hex2i) as $num
        | ($num | digital_root) as $dr
        | . + [[ $word, $num, $dr]])
  | sort_by( .[-1] )
  | . as $details

  | (reduce .[] as $line ([];
       if $line[0] | explode | unique | length >= 4
       then . + [$line] else . end)) as $digits4

  | "\($details|length) hex words with 4 or more letters were found:",
    ($details[] | format),
    "",
    "\($digits4|length) such words contain 4 or more different letters:",
     (($digits4|sort_by(.[1])|reverse[] ) | format) ;

task
Output:

Invocation

< unixdict.txt jq -Rrn -f rc-hex-words.jq
26 hex words with 4 or more letters were found:
   ababa ->    703162 -> 1
    abbe ->     43966 -> 1
    dada ->     56026 -> 1
    deaf ->     57007 -> 1
  decade ->  14600926 -> 1
    cede ->     52958 -> 2
    feed ->     65261 -> 2
    abed ->     44013 -> 3
   added ->    712173 -> 3
    bade ->     47838 -> 3
   beebe ->    782014 -> 4
   decca ->    912586 -> 4
    dade ->     56030 -> 5
    bead ->     48813 -> 6
  deface ->  14613198 -> 6
    babe ->     47806 -> 7
    fade ->     64222 -> 7
    dead ->     57005 -> 8
  efface ->  15727310 -> 8
  facade ->  16435934 -> 8
  accede ->  11325150 -> 9
    beef ->     48879 -> 9
    cafe ->     51966 -> 9
   dacca ->    896202 -> 9
    deed ->     57069 -> 9
    face ->     64206 -> 9

13 such words contain 4 or more different letters:
  facade ->  16435934 -> 8
  efface ->  15727310 -> 8
  deface ->  14613198 -> 6
  decade ->  14600926 -> 1
  accede ->  11325150 -> 9
   decca ->    912586 -> 4
    fade ->     64222 -> 7
    face ->     64206 -> 9
    deaf ->     57007 -> 1
    cafe ->     51966 -> 9
    bead ->     48813 -> 6
    bade ->     47838 -> 3
    abed ->     44013 -> 3

Julia[edit]

digroot(n) = (while n > 9 n = sum(digits(n)) end; n)

function hexwords(wordfile = "unixdict.txt")
    words = lowercase.(split(read(wordfile, String), r"\s+"))
    filter!(w -> length(w) >= 4 && all(c -> c in "abcdef", w), words)
    results = [[w, parse(Int, w, base = 16)] for w in words]
    for a in results
        pushfirst!(a, digroot(a[2]))
    end
    println("Hex words in $wordfile:\nRoot  Word      Base 10\n", "-"^30)
    for a in sort!(results)
        println(rpad(a[1], 6), rpad(a[2], 10), a[3])
    end
    println("Total count of these words: $(length(results)).")
    println("\nHex words with > 3 distinct letters:\nRoot  Word      Base 10\n", "-"^30)
    filter!(a -> length(unique(a[2])) > 3, results)
    for a in results
        println(rpad(a[1], 6), rpad(a[2], 10), a[3])
    end
    println("Total count of those words: $(length(results)).")
end

hexwords()
Output:
Hex words in unixdict.txt:
Root  Word      Base 10
------------------------------
1     ababa     703162
1     abbe      43966
1     dada      56026
1     deaf      57007
1     decade    14600926
2     cede      52958
2     feed      65261
3     abed      44013
3     added     712173
3     bade      47838
4     beebe     782014
4     decca     912586
5     dade      56030
6     bead      48813
6     deface    14613198
7     babe      47806
7     fade      64222
8     dead      57005
8     efface    15727310
8     facade    16435934
9     accede    11325150
9     beef      48879
9     cafe      51966
9     dacca     896202
9     deed      57069
9     face      64206
Total count of these words: 26.

Hex words with > 3 distinct letters:
Root  Word      Base 10
------------------------------
1     deaf      57007
1     decade    14600926
3     abed      44013
3     bade      47838
4     decca     912586
6     bead      48813
6     deface    14613198
7     fade      64222
8     efface    15727310
8     facade    16435934
9     accede    11325150
9     cafe      51966
9     face      64206
Total count of those words: 13.

Perl[edit]

#!/usr/bin/perl

use strict; # https://rosettacode.org/wiki/Hex_words
use warnings;
use List::AllUtils qw( nsort_by uniq );

sub root
  {
  local $_ = shift;
  $_ = eval while s/\B/+/g;
  return $_;
  }

my @bydecimal = grep +(uniq /[a-f]/g)[3],
  my @byroot = map { sprintf "%10s %10d %3d\n", $_, hex $_, root hex $_
  } do { local(@ARGV, $/) = 'unixdict.txt'; <> =~ /^[a-f]{4,}$/gm };

print +(nsort_by { (split ' ')[2] } @byroot),
  "total count = @{[ scalar @byroot ]} and @{[ scalar @bydecimal
  ]} have at least 4 distinct digits\n",
  reverse nsort_by { (split ' ')[1] } @bydecimal;
Output:
     ababa     703162   1
      abbe      43966   1
      dada      56026   1
      deaf      57007   1
    decade   14600926   1
      cede      52958   2
      feed      65261   2
      abed      44013   3
     added     712173   3
      bade      47838   3
     beebe     782014   4
     decca     912586   4
      dade      56030   5
      bead      48813   6
    deface   14613198   6
      babe      47806   7
      fade      64222   7
      dead      57005   8
    efface   15727310   8
    facade   16435934   8
    accede   11325150   9
      beef      48879   9
      cafe      51966   9
     dacca     896202   9
      deed      57069   9
      face      64206   9
total count = 26 and 13 have at least 4 distinct digits
    facade   16435934   8
    efface   15727310   8
    deface   14613198   6
    decade   14600926   1
    accede   11325150   9
     decca     912586   4
      fade      64222   7
      face      64206   9
      deaf      57007   1
      cafe      51966   9
      bead      48813   6
      bade      47838   3
      abed      44013   3

Phix[edit]

with javascript_semantics
function af(string s) return max(s)<='f' and min(s)>='a' end function
function digital_root(integer n)
    assert(n>=0)
    while n>9 do
        integer tot = 0
        while n>0 do
            tot += remainder(n,10)
            n = floor(n/10)
        end while
        n = tot
    end while
    return n
end function
sequence words = filter(unix_dict(4),af),
         decml = apply(true,to_integer,{words,0,16}),
         roots = apply(decml,digital_root),
         tags = custom_sort(roots,tagset(length(roots)))

function caejasp() -- columnize/apply/extract/join/apply/sprint(...)
    sequence s = columnize(apply(true,extract,{{words,decml,roots},{tags}}))
    return join(apply(true,sprintf,{{"%6s -> %8d -> %d\n"},s}))
end function
printf(1," %s\n %d hex words with 4 or more letters found,\n",{caejasp(),length(tags)})
function ge4(integer t) return length(unique(words[t]))>=4 end function
tags = filter(tags,ge4)
tags = extract(tags,reverse(custom_sort(extract(decml,tags),tagset(length(tags)))))
printf(1," %d with 4 or more distinct characters:\n\n %s\n",{length(tags),caejasp()})
Output:
  ababa ->   703162 -> 1
   abbe ->    43966 -> 1
   dada ->    56026 -> 1
   deaf ->    57007 -> 1
 decade -> 14600926 -> 1
   cede ->    52958 -> 2
   feed ->    65261 -> 2
   abed ->    44013 -> 3
  added ->   712173 -> 3
   bade ->    47838 -> 3
  beebe ->   782014 -> 4
  decca ->   912586 -> 4
   dade ->    56030 -> 5
   bead ->    48813 -> 6
 deface -> 14613198 -> 6
   babe ->    47806 -> 7
   fade ->    64222 -> 7
   dead ->    57005 -> 8
 efface -> 15727310 -> 8
 facade -> 16435934 -> 8
 accede -> 11325150 -> 9
   beef ->    48879 -> 9
   cafe ->    51966 -> 9
  dacca ->   896202 -> 9
   deed ->    57069 -> 9
   face ->    64206 -> 9

 26 hex words with 4 or more letters found,
 13 with 4 or more distinct characters:

 facade -> 16435934 -> 8
 efface -> 15727310 -> 8
 deface -> 14613198 -> 6
 decade -> 14600926 -> 1
 accede -> 11325150 -> 9
  decca ->   912586 -> 4
   fade ->    64222 -> 7
   face ->    64206 -> 9
   deaf ->    57007 -> 1
   cafe ->    51966 -> 9
   bead ->    48813 -> 6
   bade ->    47838 -> 3
   abed ->    44013 -> 3

Python[edit]

def digroot(n):
    while n > 9:
        n = sum([int(d) for d in str(n)])
    return n

with open('unixdict.txt') as f:
    lines = [w.strip() for w in f.readlines()]
    words = [w for w in lines if len(w) >= 4 and all(c in 'abcdef' for c in w)]
    results = [[w, int(w, 16)] for w in words]
    for a in results:
        a.append(digroot(a[1]))
        
    print(f"Hex words in unixdict.txt:\nRoot  Word      Base 10\n", "-"*22)
    for a in sorted(results, key=lambda x:x[2]):
        print(f"{a[2]}     {a[0]:6}{a[1]:10}")
        
    print("Total count of these words:", len(results))
    print("\nHex words with > 3 distinct letters:\nRoot  Word      Base 10\n", "-"*22)
    results = [a for a in results if len(set(str(a[0]))) > 3]
    for a in sorted(results, key=lambda x:x[2]):
        print(f"{a[2]}     {a[0]:6}{a[1]:10}")
        
    print("Total count of those words:", len(results))
Output:
Hex words in unixdict.txt:
Root  Word      Base 10
 ----------------------
1     ababa     703162
1     abbe       43966
1     dada       56026
1     deaf       57007
1     decade  14600926
2     cede       52958
2     feed       65261
3     abed       44013
3     added     712173
3     bade       47838
4     beebe     782014
4     decca     912586
5     dade       56030
6     bead       48813
6     deface  14613198
7     babe       47806
7     fade       64222
8     dead       57005
8     efface  15727310
8     facade  16435934
9     accede  11325150
9     beef       48879
9     cafe       51966
9     dacca     896202
9     deed       57069
9     face       64206
Total count of these words: 26

Hex words with > 3 distinct letters:
Root  Word      Base 10
 ----------------------
1     deaf       57007
1     decade  14600926
3     abed       44013
3     bade       47838
4     decca     912586
6     bead       48813
6     deface  14613198
7     fade       64222
8     efface  15727310
8     facade  16435934
9     accede  11325150
9     cafe       51966
9     face       64206
Total count of those words: 13

Quackery[edit]

  [ 1 - 9 mod 1+ ]          is digitalroot (   n --> n )

  [ 0 swap witheach
      [ bit | ]
    0 swap
    [ dup while
      tuck 1 & +
      swap 1 >>
      again ]
    drop ]                  is uniques     (   $ --> n )

  [ tuck space swap of
    join
    swap split drop echo$ ] is lecho$      ( $ n -->   )


  $ 'rosetta/unixdict.txt' sharefile drop
  nest$
  [] swap witheach
    [ dup size 4 < iff drop done
      true over witheach
        [ char a char g within
          not if [ conclude not ] ]
      iff [ nested join ]
      else drop ]
  16 base put
  [] swap witheach
    [ dup dip nested
      $->n drop join
      nested join ]
  base release
  [] swap witheach
    [ dup 1 peek
      digitalroot
      join nested join ]
  dup
  sortwith [ 2 peek swap 2 peek < ]
  witheach
    [ unpack echo sp
      swap 6 lecho$
      sp echo cr ]
  dup size echo say " words"
  cr cr
  [] swap witheach
    [ dup 0 peek
      uniques 4 < iff
        drop
      else [ nested join ] ]
  sortwith [ 1 peek swap 1 peek > ]
  dup
  witheach
    [ unpack echo sp
      swap 6 lecho$
      sp echo cr ]
  size echo say " words"
Output:
1 ababa  703162
1 abbe   43966
1 dada   56026
1 deaf   57007
1 decade 14600926
2 cede   52958
2 feed   65261
3 abed   44013
3 added  712173
3 bade   47838
4 beebe  782014
4 decca  912586
5 dade   56030
6 bead   48813
6 deface 14613198
7 babe   47806
7 fade   64222
8 dead   57005
8 efface 15727310
8 facade 16435934
9 accede 11325150
9 beef   48879
9 cafe   51966
9 dacca  896202
9 deed   57069
9 face   64206
26 words

8 facade 16435934
8 efface 15727310
6 deface 14613198
1 decade 14600926
9 accede 11325150
4 decca  912586
7 fade   64222
9 face   64206
1 deaf   57007
9 cafe   51966
6 bead   48813
3 bade   47838
3 abed   44013
13 words

Raku[edit]

Sorted by digital root with a secondary alphabetical sort.

sub dr (Int $_ is copy) { $_ = dr(.comb.sum) while .chars > 1; $_ }

my %hex = './unixdict.txt'.IO.slurp.words.grep( *.chars > 3 )\
  .grep({ not / <-[abcdef]> / }).map: { $_ => dr :16($_).comb.sum }

say "{+%hex} hex words longer than 3 characters found in unixdict.txt:";
printf "%6s ➡ %8d ➡ %d\n", .key, :16(.key), .value for %hex.sort: { .value, .key }

my %many = %hex.grep: +*.key.comb.Set > 3;

say "\nOf which {+%many} contain at least four distinct characters:";
printf "%6s ➡ %8d ➡ %d\n", .key, :16(.key), .value for %many.sort: { -:16(.key) }
Output:
26 hex words longer than 3 characters found in unixdict.txt:
 ababa ➡   703162 ➡ 1
  abbe ➡    43966 ➡ 1
  dada ➡    56026 ➡ 1
  deaf ➡    57007 ➡ 1
decade ➡ 14600926 ➡ 1
  cede ➡    52958 ➡ 2
  feed ➡    65261 ➡ 2
  abed ➡    44013 ➡ 3
 added ➡   712173 ➡ 3
  bade ➡    47838 ➡ 3
 beebe ➡   782014 ➡ 4
 decca ➡   912586 ➡ 4
  dade ➡    56030 ➡ 5
  bead ➡    48813 ➡ 6
deface ➡ 14613198 ➡ 6
  babe ➡    47806 ➡ 7
  fade ➡    64222 ➡ 7
  dead ➡    57005 ➡ 8
efface ➡ 15727310 ➡ 8
facade ➡ 16435934 ➡ 8
accede ➡ 11325150 ➡ 9
  beef ➡    48879 ➡ 9
  cafe ➡    51966 ➡ 9
 dacca ➡   896202 ➡ 9
  deed ➡    57069 ➡ 9
  face ➡    64206 ➡ 9

Of which 13 contain at least four distinct characters:
facade ➡ 16435934 ➡ 8
efface ➡ 15727310 ➡ 8
deface ➡ 14613198 ➡ 6
decade ➡ 14600926 ➡ 1
accede ➡ 11325150 ➡ 9
 decca ➡   912586 ➡ 4
  fade ➡    64222 ➡ 7
  face ➡    64206 ➡ 9
  deaf ➡    57007 ➡ 1
  cafe ➡    51966 ➡ 9
  bead ➡    48813 ➡ 6
  bade ➡    47838 ➡ 3
  abed ➡    44013 ➡ 3

V (Vlang)[edit]

Translation of: wren
import os
import strconv
import math

fn digital_root(nn i64) i64 {
    mut n := nn
    for n>9 {
        mut tot := i64(0)
        for n>0 {
            tot += n%10
            n = int(math.floor(n/10))
        }
        n = tot
    }
    return n
}

fn main() {
    hex_digits := 'abcdef'
    words := os.read_lines('unixdict.txt')?
    mut lines := [][]string{}
    //println(words)
    for word in words {
        if word.len < 4 {
            continue
        }
        if word.split('').all(hex_digits.index(it) or {-1} >= 0) {
            num := strconv.parse_int(word, 16, 32) or {-1}
            lines << [word, num.str(), digital_root(num).str()]
        }
    }
    lines.sort_with_compare(fn(mut a []string, mut b []string) int {
        if a[2].int()<b[2].int(){
            return -1
        }else if a[2].int()>b[2].int(){
            return 1
        }
        return 0
    })
    for line in lines {
        println('${line[0]:8} -> ${line[1]:9} -> ${line[2]}')
    }
    println('$lines.len hex words with 4 or more letters found')
    lines = lines.filter(fn (a []string) bool {
        mut s := map[string]bool{}
        for t in a[0].split('') {
            if t !in s {
                s[t] = true
            }
        }
        return s.len > 3
    })
    for line in lines {
        println('${line[0]:8} -> ${line[1]:9} -> ${line[2]}')
    }
    println('$lines.len hex words with 4 or more distinct letters found')
}
Output:
Same as wren entry

Wren[edit]

Library: Wren-ioutil
Library: Wren-fmt
Library: Wren-math
Library: Wren-seq
import "./ioutil" for FileUtil
import "./fmt" for Conv, Fmt
import "./math" for Int
import "./seq" for Lst

var words = FileUtil.readLines("unixdict.txt").where { |w| w.count > 0 && w[0].bytes[0] < 103 }
var hexDigits = "abcdef"
var lines = []
for (word in words) {
    if (word.count < 4) continue
    if (word.all { |c| hexDigits.contains(c) }) {
        var num = Conv.atoi(word, 16)
        var dr = Int.digitalRoot(num)[0] 
        lines.add(Fmt.swrite("$-7s -> $-9s -> $d", word, num, dr))
    }
}
lines.sort { |a, b| a[-1].bytes[0] < b[-1].bytes[0] }
System.print(lines.join("\n"))
System.print("\n%(lines.count) hex words with 4 or more letters found.\n")
var digits4 = []
for (line in lines) {
    var word = line.split("->")[0].trimEnd()
    if (Lst.distinct(word.toList).count >= 4) digits4.add(line)
}
digits4.sort { |a, b| Num.fromString(a.split("->")[1]) > Num.fromString(b.split("->")[1]) }
System.print(digits4.join("\n"))
System.print("\n%(digits4.count) such words found which contain 4 or more different digits.")
Output:
decade  -> 14600926  -> 1
ababa   -> 703162    -> 1
abbe    -> 43966     -> 1
dada    -> 56026     -> 1
deaf    -> 57007     -> 1
feed    -> 65261     -> 2
cede    -> 52958     -> 2
added   -> 712173    -> 3
bade    -> 47838     -> 3
abed    -> 44013     -> 3
decca   -> 912586    -> 4
beebe   -> 782014    -> 4
dade    -> 56030     -> 5
deface  -> 14613198  -> 6
bead    -> 48813     -> 6
babe    -> 47806     -> 7
fade    -> 64222     -> 7
dead    -> 57005     -> 8
facade  -> 16435934  -> 8
efface  -> 15727310  -> 8
dacca   -> 896202    -> 9
beef    -> 48879     -> 9
cafe    -> 51966     -> 9
deed    -> 57069     -> 9
face    -> 64206     -> 9
accede  -> 11325150  -> 9

26 hex words with 4 or more letters found.

facade  -> 16435934  -> 8
efface  -> 15727310  -> 8
deface  -> 14613198  -> 6
decade  -> 14600926  -> 1
accede  -> 11325150  -> 9
decca   -> 912586    -> 4
fade    -> 64222     -> 7
face    -> 64206     -> 9
deaf    -> 57007     -> 1
cafe    -> 51966     -> 9
bead    -> 48813     -> 6
bade    -> 47838     -> 3
abed    -> 44013     -> 3

13 such words found which contain 4 or more different digits.