Hex words

From Rosetta Code
Task
Hex words
You are encouraged to solve this task according to the task description, using any language you may know.
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

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

Ada

-- Hex Words: find words of 4 or more characters, all characters of which are hex digits
-- J. Carter     2024 May

with Ada.Characters.Handling;
with Ada.Containers.Vectors;
with Ada.Strings.Unbounded;
with Ada.Text_IO;

procedure Hex_Words is
   use Ada.Strings.Unbounded;

   subtype Hex_Digit is Character range 'a' .. 'f';
   subtype Digit_Value is Integer range 0 .. 9;

   function Hex_Word (Line : in String) return Boolean is
      (Line'Length > 3 and (for all C of Line => C in Hex_Digit) );

   function Digital_Root (Number : in Natural) return Digit_Value;
   -- Returns the decimal digital root of Number

   function Four_Distinct (Word : in String) return Boolean with
      Pre => Hex_Word (Word);
   -- Returns True if Word has at least 4 distinct letters; False otherwise

   function Image (Number : in Natural) return String with
      Post => Image'Result'Length = 9;
   -- Returns the blank-filled decimal image of Number

   type Word_Info is record
      Word  : Unbounded_String;
      Value : Natural;
      Root  : Digit_Value;
   end record;

   function Root_Less (Left : in Word_Info; Right : in Word_Info) return Boolean is
      (if Left.Root /= Right.Root then Left.Root < Right.Root else Left.Word < Right.Word);

   function Value_Greater (Left : in Word_Info; Right : in Word_Info) return Boolean is
      (if Left.Value /= Right.Value then Left.Value > Right.Value else Left.Word < Right.Word);

   package Word_Lists is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => Word_Info);

   package Root_Sorting is new Word_Lists.Generic_Sorting ("<" => Root_Less);
   package Value_Sorting is new Word_Lists.Generic_Sorting ("<" => Value_Greater);

   function Digital_Root (Number : in Natural) return Digit_Value is
      function Digit_Sum return Natural;
      -- Sums the digits of the decimal representation of Number

      function Digit_Sum return Natural is
         Image : constant String := Number'Image;

         Sum   : Natural := 0;
      begin -- Digit_Sum
         All_Digits : for I in 2 .. Image'Last loop
            Sum := Sum + Character'Pos (Image (I) ) - Character'Pos ('0');
         end loop All_Digits;

         return Sum;
      end Digit_Sum;

      Sum : Natural := Digit_Sum;
   begin -- Digital_Root
      if Sum in Digit_Value then
         return Sum;
      end if;

      return Digital_Root (Sum);
   end Digital_Root;

   function Four_Distinct (Word : in String) return Boolean is
      type Hex_Set is array (Hex_Digit) of Boolean;

      Set   : Hex_Set := (others => False);
      Count : Natural := 0;
   begin -- Four_Distinct
      Check_All : for C of Word loop
         Set (C) := True;
      end loop Check_All;

      Count_Them : for B of Set loop
         if B then
            Count := Count + 1;
         end if;
      end loop Count_Them;

      return Count > 3;
   end Four_Distinct;

   function Image (Number : in Natural) return String is
      Result : constant String := Number'Image;
   begin -- Image
      return (1 .. 9 - Result'Length => ' ') & Result;
   end Image;

   Input    : Ada.Text_IO.File_Type;
   Info     : Word_Info;
   Word     : Word_Lists.Vector;
   Distinct : Word_Lists.Vector;
begin -- Hex_Words
   Ada.Text_IO.Open (File => Input, Mode => Ada.Text_IO.In_File, Name => "unixdict.txt");

   All_Words : loop
      exit All_Words when Ada.Text_IO.End_Of_File (Input);

      One_Word : declare
         Line : constant String := Ada.Characters.Handling.To_Lower (Ada.Text_IO.Get_Line (Input) );
      begin -- One_Word
         if Hex_Word (Line) then
            Info.Word := To_Unbounded_String (Line);
            Info.Value := Integer'Value ("16#" & Line & '#');
            Info.Root := Digital_Root (Info.Value);
            Word.Append (New_Item => Info);

            if Four_Distinct (Line) then
               Distinct.Append (New_Item => Info);
            end if;
         end if;
      end One_Word;
   end loop All_Words;

   Ada.Text_IO.Close (File => Input);

   Root_Sorting.Sort (Container => Word);
   Value_Sorting.Sort (Container => Distinct);

   Print_All : for I in 1 .. Word.Last_Index loop
      Print_One : declare
         Info : Word_Info renames Word.Element (I);
      begin -- Print_One
         Ada.Text_IO.Put_Line
            (Item => To_String (Info.Word) & (1 .. 6 - Length (Info.Word) => ' ') & Image (Info.Value) & Info.Root'Image);
      end Print_One;
   end loop Print_All;

   Ada.Text_IO.Put_Line (Item => Word.Last_Index'Image & " total words");
   Ada.Text_IO.New_Line;

   Output_Distinct : for I in 1 ..Distinct.Last_Index loop
      One_Distinct : declare
         Info : Word_Info renames Distinct.Element (I);
      begin -- One_Distinct
         Ada.Text_IO.Put_Line
            (Item => To_String (Info.Word) & (1 .. 6 - Length (Info.Word) => ' ') & Image (Info.Value) & Info.Root'Image);
      end One_Distinct;
   end loop Output_Distinct;

   Ada.Text_IO.Put_Line (Item => Distinct.Last_Index'Image & " words with 4 or more distinct letters");
end Hex_Words;
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 total 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
 13 words with 4 or more distinct letters

ALGOL 68

Note the source of the RC library ALGOL 68-files is on a separate page on Rosetta Code, see the above link.

BEGIN # find words with 4 or more characters that contain only hex digits a-f #

    PR read "files.incl.a68" PR                      # include file utilities #

    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;

    # returns TRUE if word is a hex-word ( contains only a-f and is at least  #
    #         four characters long, FALSE otherwise                           #
    # if word is a hex-word, it is stored in the hw table                     #
    # count so far contains the count of preceding hex-words                  #
    PROC find hex words = ( STRING word, INT count so far )BOOL:
         IF INT word len = ( UPB word + 1 ) - LWB word;
            word len < 4
         THEN FALSE                                        # word is too short#
         ELSE                            # 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 NOT is hex word
            THEN FALSE                                       # not a hex word #
            ELSE                                            # have a hex word #
                LONG INT r := int word;            # compute the digital root #
                WHILE r > 9 DO
                    LONG INT dr := r MOD 10;
                    WHILE ( r OVERAB 10 ) > 0 DO dr +:= r MOD 10 OD;
                    r := dr
                OD;
                INT hw pos = count so far + 1;
                word  OF hw[ hw pos ] := word;
                value OF hw[ hw pos ] := int word;
                root  OF hw[ hw pos ] := SHORTEN r;
                len   OF hw[ hw pos ] := word len;
                TRUE
            FI
         FI # find hex words # ;

    # prints the HEXWORD hw                                                   #
    PROC show hex word = ( HEXWORD hw )VOID:
         BEGIN
            print( ( word OF hw, ": " ) );
            TO 12 - len OF hw DO print( ( " " ) ) OD;
            print( ( whole( value OF hw, -10 ) ) );
            print( ( " [ ", whole( root OF hw, 0 ), " ]", newline ) )
         END # show hex word # ;
    # Quicksorts in-place the array of HEXWORDS a, from lb to ub              #
    # on ascending value                                                      #
    PROC quicksort hw = ( 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 hw( a, lb,   right );
            quicksort hw( a, left, ub    )
         FI # quicksort hw # ;

    # load hw with the hex-words from unixdict.txt                            #
    IF   INT count = "unixdict.txt" EACHLINE find hex words;
         count < 0
    THEN print( ( "Unable to open unixdict.txt", newline ) )
    ELSE # 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 hex word( 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( 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 hex word( hw[ i ] )
             FI
         OD;
         print( ( "Found ", whole( count 4, 0 ) ) );
         print( ( " hex words with 4 or more distinct digits", newline ) )
    FI

END
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

APL

Works with: Dyalog APL
HexWords;todec;digroot;displayrow;words;distinct4
todec169+'abcdef'
digroot(+/10¯1)(10≥⊢)
displayrow{ n (digrootntodec )}

words((~∊)⎕TC⊆⊢)⎕NGET'unixdict.txt'
words(words.¨⊂⊂'abcdef')/words
words(4≤≢¨words)/words
wordswords[digroottodec¨words]

distinct4(4≤≢¨words)/words
distinct4distinct4[todec¨distinct4]

(⍕≢words),' hex words with at least 4 letters in unixdict.txt:'
displayrow¨words
''

(⍕≢distinct4),' hex words with at least 4 distinct letters:'
displayrow¨distinct4

Output:
26 hex words with at least 4 letters 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

13 hex words with at least 4 distinct 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

AppleScript

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" -- <www.macscripter.net/t/timsort-and-nigsort/71383/3>

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 ca to current application
    set unixdictPath to (path to desktop as text) & "unixdict.txt"
    set allWords to (read (unixdictPath as «class furl») from 1 as «class utf8»)'s words
    set hexwordFilter to ca's class "NSPredicate"'s ¬
        predicateWithFormat:("self MATCHES '^[a-f]{4,}+$'")
    set hexwords to ((ca'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)
        set root to digitalRoot(decimal, 10)
        set thisEntry to {thisWord, decimal, root}
        set end of list1 to thisEntry
        set distinctChars to (ca'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

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

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

# 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

BBC BASIC

      INSTALL @lib$ + "SORTLIB"
      sort%=FN_sortinit(0, 0)

      DIM Result$(127)
      *LOWERCASE ON
      F%=OPENIN(@dir$ + "unixdict.txt")
      WHILE TRUE
        W$=GET$#F%
        IF W$ < "g" ELSE EXIT WHILE
        IF LENW$ > 3 IF INSTR(W$, "o") == 0 THEN
          D%=EVAL("&" + W$)
          IF LENW$ == LEN(STR$~D%) THEN
            REPEAT
              E%=0
              WHILE D% > 0 E%+=D% MOD 10 D%/=10 ENDWHILE
              D%=E%
            UNTIL D% < 10
            Result$(C%)=STR$D% + W$
            C%+=1
          ENDIF
        ENDIF
      ENDWHILE
      CLOSE#F%

      CALL sort%, Result$(0)
      PRINT "Root  Word      Base 10"
      FOR I%=0 TO C% - 1
        W$=MID$(Result$(I%), 2)
        PRINT "  " LEFT$(Result$(I%), 1) "   " W$ TAB(13) EVAL("&" + W$)
        E%=0
        FOR J%=ASC"a" TO ASC"f"
          IF INSTR(W$, CHR$J%) E%+=1
        NEXT
        IF E% > 3 THEN
          Result$(I%)="z" + STR$LENResult$(I%) + W$ + LEFT$(Result$(I%), 1)
          N%+=1
        ENDIF
      NEXT
      PRINT "Total: ";C% '

      CALL sort%, Result$(0)
      PRINT "Root  Word      Base 10"
      FOR I%=C% - 1 TO C% - N% STEP -1
        W$=LEFT$(MID$(Result$(I%), 3))
        PRINT "  " RIGHT$(Result$(I%)) "   " W$ TAB(13) EVAL("&" + W$)
      NEXT
      PRINT "Total: ";N%
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: 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: 13

C++

#include <algorithm>
#include <cstdint>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>

struct Item {
	std::string word;
	int32_t number;
	int32_t digital_root;
};

void display(const std::vector<Item>& items) {
	std::cout << "  Word      Decimal value   Digital root" << std::endl;
	std::cout << "----------------------------------------" << std::endl;
	for ( const Item& item : items ) {
		std::cout << std::setw(7) << item.word << std::setw(15) << item.number
				  << std::setw(12) << item.digital_root << std::endl;
	}
	std::cout << "\n" << "Total count: " << items.size() << "\n" << std::endl;
}

int32_t digital_root(int32_t number) {
	int32_t result = 0;
	while ( number > 0 ) {
		result += number % 10;
		number /= 10;
	}
	return ( result <= 9 ) ? result : digital_root(result);
}

bool contains_only(const std::string& word, const std::unordered_set<char>& acceptable) {
    return std::all_of(word.begin(), word.end(),
    	[acceptable](char ch) { return acceptable.find(ch) != acceptable.end(); });
}

int main() {
	const std::unordered_set<char> hex_digits{ 'a', 'b', 'c', 'd', 'e', 'f' };
	std::vector<Item> items;

	std::fstream file_stream;
	file_stream.open("unixdict.txt");
	std::string word;
	while ( file_stream >> word ) {
		if ( word.length() >= 4 && contains_only(word, hex_digits)) {
			const int32_t value = std::stoi(word, 0, 16);
			int32_t root = digital_root(value);
			items.push_back(Item(word, value, root));
		}
	}

	auto compare = [](Item a, Item b) {
		return ( a.digital_root == b.digital_root ) ? a.word < b.word : a.digital_root < b.digital_root;
	};
	std::sort(items.begin(), items.end(), compare);
	display(items);

	std::vector<Item> filtered_items;
	for ( const Item& item : items ) {
		if ( std::unordered_set<char>(item.word.begin(), item.word.end()).size() >= 4 ) {
			filtered_items.push_back(item);
		}
	}

	auto comp = [](Item a, Item b) { return a.number > b.number; };
	std::sort(filtered_items.begin(), filtered_items.end(), comp);
	display(filtered_items);
}
Output:
  Word      Decimal value   Digital 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

Total count: 26

  Word      Decimal value   Digital 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

Total count: 13

EasyLang

func ishex w$ .
   c$ = ""
   for c$ in strchars w$
      h = strcode c$
      if h < 97 or h > 102
         break 1
      .
   .
   return if c$ = ""
.
global w$[] part2 .
func digsum num .
   while num > 0
      s += num mod 10
      num = num div 10
   .
   return s
.
func digroot x .
   while x > 9
      x = digsum x
   .
   return x
.
func dec w$ .
   for c$ in strchars w$
      r = r * 16 + strcode c$ - 87
   .
   return r
.
func comp a$ b$ .
   if part2 = 1
      return if dec a$ > dec b$
   .
   return if digroot dec a$ < digroot dec b$
.
proc sort . .
   for i = len w$[] - 1 downto 1
      for j = 1 to i
         if comp w$[j] w$[j + 1] = 1
            swap w$[j] w$[j + 1]
         .
      .
   .
.
proc init . .
   repeat
      s$ = input
      until s$ = ""
      if len s$ >= 4 and ishex s$ = 1
         w$[] &= s$
      .
   .
.
init
sort
proc show . .
   for w$ in w$[]
      w = dec w$
      print digroot w & " " & w$ & " " & w
   .
   print len w$[] & " words"
   print ""
.
show
# 
func dist w$ .
   len d[] 6
   for c$ in strchars w$
      d[strcode c$ - 96] = 1
   .
   for e in d[]
      s += e
   .
   return s
.
for w$ in w$[]
   if dist w$ > 3
      w2$[] &= w$
   .
.
swap w$[] w2$[]
part2 = 1
sort
show
# 
# the content of unixdict.txt 
input_data
10th
ababa
beebe
decade
facade

Factor

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.

FreeBASIC

Function DigitalRoot(n As Integer) As Integer
    While n > 9
        Dim s As String = Str(n)
        n = 0
        For i As Integer = 1 To Len(s)
            n += Val(Mid(s, i, 1))
        Next
    Wend
    Return n
End Function

Sub BubbleSort(arr() As String)
    For i As Integer = 1 To Ubound(arr) - 1
        For j As Integer = 1 To Ubound(arr) - i - 1
            If arr(j) > arr(j + 1) Then Swap arr(j), arr(j + 1)
        Next
    Next
End Sub

Sub ReadLines(filename As String, lines() As String)
    Open filename For Input As #1
    Dim linea As String
    Dim lineCount As Integer = 0
    Do While Not Eof(1)
        Line Input #1, linea
        lineCount += 1
        Redim Preserve lines(lineCount)
        lines(lineCount - 1) = linea
    Loop
    Close #1
End Sub

Sub FilterWords(lines() As String, words() As String)
    Dim As Integer i, j
    Dim wordCount As Integer = 0
    For i = 0 To Ubound(lines)
        Dim w As String = Trim(lines(i))
        If Len(w) >= 4 Then
            Dim valid As Boolean = True
            For j = 1 To Len(w)
                If Instr("abcdef", Mid(w, j, 1)) = 0 Then
                    valid = False
                    Exit For
                End If
            Next
            If valid Then
                wordCount += 1
                Redim Preserve words(wordCount)
                words(wordCount - 1) = w
            End If
        End If
    Next
End Sub

Sub CalculateResults(words() As String, results() As String)
    Dim resultCount As Integer = 0
    For i As Integer = 0 To Ubound(words)-1
        Dim w As String = words(i)
        Dim base10 As Integer = Val("&H" & w)
        Dim root As Integer = DigitalRoot(base10)
        resultCount += 1
        Redim Preserve results(resultCount)
        results(resultCount - 1) = Str(root) & " " & w & " " & Str(base10)
    Next
End Sub

Sub PrintResults(results() As String, title As String)
    Dim As Integer i, j
    Print title
    Print "Root  Word      Base 10"
    Print String(23, "-")
    BubbleSort(results())
    For i = 0 To Ubound(results) -1
        Dim parts() As String
        Dim part As String = ""
        Dim partIndex As Integer = 0
        For j = 1 To Len(results(i))
            If Mid(results(i), j, 1) = " " Then
                partIndex += 1
                Redim Preserve parts(partIndex)
                parts(partIndex - 1) = part
                part = ""
            Else
                part &= Mid(results(i), j, 1)
            End If
        Next
        partIndex += 1
        Redim Preserve parts(partIndex)
        parts(partIndex - 1) = part
        
        Print Using "##    \    \   ########"; Val(parts(0)); parts(1); Val(parts(2))
    Next
    Print "Total count of these words: "; Ubound(results)
End Sub

Sub FilterDistinctLetters(results() As String, filteredResults() As String)
    Dim As Integer i, j
    Dim filteredCount As Integer = 0
    For i = 0 To Ubound(results)
        Dim parts() As String
        Dim part As String = ""
        Dim partIndex As Integer = 0
        For j = 1 To Len(results(i))
            If Mid(results(i), j, 1) = " " Then
                partIndex += 1
                Redim Preserve parts(partIndex)
                parts(partIndex - 1) = part
                part = ""
            Else
                part &= Mid(results(i), j, 1)
            End If
        Next
        partIndex += 1
        Redim Preserve parts(partIndex)
        parts(partIndex - 1) = part
        
        Dim w As String = parts(1)
        If Len(w) > 3 Then
            Dim distinct As String = ""
            For j = 1 To Len(w)
                If Instr(distinct, Mid(w, j, 1)) = 0 Then
                    distinct &= Mid(w, j, 1)
                End If
            Next
            If Len(distinct) > 3 Then
                filteredCount += 1
                Redim Preserve filteredResults(filteredCount)
                filteredResults(filteredCount - 1) = results(i)
            End If
        End If
    Next
End Sub

Dim lines() As String
Dim words() As String
Dim results() As String
Dim filteredResults() As String

' Main program
ReadLines("unixdict.txt", lines())
FilterWords(lines(), words())
CalculateResults(words(), results())

PrintResults(results(), "Hex words in unixdict.txt:")
Print

FilterDistinctLetters(results(), filteredResults())
PrintResults(filteredResults(), "Hex words with > 3 distinct letters:")

Sleep
Output:
Same as Python entry.

FutureBasic

#plist NSAppTransportSecurity @{NSAllowsArbitraryLoads:YES}

include "NSLog.incl"

local fn ConvertHexToInt( hexNumberStr as CFStringRef ) as NSUInteger
  NSUInteger  outVal = 0
  ScannerRef scanner = fn ScannerWithString( hexNumberStr )
  fn ScannerScanHexInt( scanner, @outVal )
end fn = outVal


local fn DigitalRoot( n as NSUInteger ) as NSUInteger
  while ( n > 9 )
    NSUInteger tot = 0
    while ( n > 0 )
      tot += n mod 10
      n = fn floor( n / 10 )
    wend
    n = tot
  wend
end fn = n


local fn HasDistinctLetters( hexNumberStr as CFStringRef ) as BOOL
  NSUInteger A = 0, B = 0, C = 0, D = 0, E = 0, F = 0, length = len( hexNumberStr )
  
  while ( length > 0 )
    length--
    unichar aChar = fn StringCharacterAtIndex( hexNumberStr, length )
    select ( aChar )
      case _"a" : if A = 0 then A = 1
      case _"b" : if B = 0 then B = 1
      case _"c" : if C = 0 then C = 1
      case _"d" : if D = 0 then D = 1
      case _"e" : if E = 0 then E = 1
      case _"f" : if F = 0 then F = 1
    end select
  wend
  if ( A + B + C + D + E + F ) > 3 then exit fn = YES
end fn = NO


local fn ParseDictionaryHexWords as CFArrayRef
  CFURLRef              url = fn URLWithString( @"http://wiki.puzzlers.org/pub/wordlists/unixdict.txt" )
  CFStringRef        string = lcase( fn StringWithContentsOfURL( url, NSUTF8StringEncoding, NULL ) )
  CFArrayRef        tempArr = fn StringComponentsSeparatedByCharactersInSet( string, fn CharacterSetNewlineSet )
  CFMutableArrayRef dictArr = fn MutableArrayNew
  CFStringRef       tempStr
  
  for tempStr in tempArr
    if ( fn StringLength( tempStr ) > 3 )  // Keep four letter words and longer
      CFRange range = fn StringRangeOfStringWithOptions( tempStr, @"^[a-f]+$", NSRegularExpressionSearch ) // Keep wordss with letters a to f
      if range.location != NSNotFound then MutableArrayAddObject( dictArr, tempStr )
    end if
  next
end fn = fn ArrayWithArray( dictArr )


local fn ConvertWordsToHexValues as CFStringRef
  CFArrayRef  hexWordArray = fn ParseDictionaryHexWords
  CFStringRef wordStr
  CFMutableArrayRef mutArr = fn MutableArrayNew //fn MutableStringWithString( @"Root   Word      Base 10\n ---------------------------\n" )
  CFMutableArrayRef lngArr = fn MutableArrayNew
  
  for wordStr in hexWordArray
    NSUInteger uintFromHex = fn ConvertHexToInt( wordStr )
    NSUInteger digitalRoot = fn DigitalRoot( uintFromHex )
    CFStringREf formatStr = fn StringWithFormat( @"%2lu     %-8s  %lu", digitalRoot, fn StringUTF8String( wordStr ), uintFromHex )
    MutableArrayAddObject( mutArr, formatStr )
    if ( fn HasDistinctLetters( wordStr ) == YES )
      MutableArrayAddObject( lngArr, formatStr )
    end if
  next
  CFStringRef headerStr = @"\nRoot   Word      Base 10\n ---------------------------\n"
  CFArrayRef  resultArr = fn ArraySortedArrayUsingSelector( mutArr, @"localizedCompare:" )
  CFStringRef resultStr = fn ArrayComponentsJoinedByString( resultArr, @"\n" )
  CFArrayRef uniquetArr = fn ArraySortedArrayUsingSelector( lngArr, @"localizedCompare:" )
  CFStringRef uniqueStr = fn ArrayComponentsJoinedByString( uniquetArr, @"\n" )
  CFStringRef  finalStr = fn StringWithFormat( @"%@%@\n\nHex words with 3 > distinct letters:%@%@", headerStr, resultStr, headerStr, uniqueStr )
end fn = finalStr

NSLog( @"%@", fn ConvertWordsToHexValues )

HandleEvents
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

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



J

   (#~$&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  

Java

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public final class HexWords {

	public static void main(String[] aArgs) throws IOException {
		Set<Character> hexDigits = Set.of( 'a', 'b', 'c', 'd', 'e', 'f' );
		
		List<Item> items = Files.lines(Path.of("unixdict.txt"))
								.filter( word -> word.length() >= 4 )
								.filter( word -> word.chars().allMatch( ch -> hexDigits.contains((char) ch) ) )			
								.map( word -> { final int value = Integer.parseInt(word, 16);
												return new Item(word, value, digitalRoot(value));
											  } )
								.collect(Collectors.toList());

		Collections.sort(items, Comparator.comparing(Item::getDigitalRoot).thenComparing(Item::getWord));	
		display(items);		
		
		List<Item> filteredItems = items.stream()
			.filter( item -> item.aWord.chars().mapToObj( ch -> (char) ch ).collect(Collectors.toSet()).size() >= 4 )
			.collect(Collectors.toList());		
		
		Collections.sort(filteredItems, Comparator.comparing(Item::getNumber).reversed());	
		display(filteredItems);
	}		

	private static int digitalRoot(int aNumber) {
	    int result = 0;
    	while ( aNumber > 0 ) {
    		result += aNumber % 10;
    		aNumber /= 10;
    	}
	    return ( result <= 9 ) ? result : digitalRoot(result);		
	}
	
	private static void display(List<Item> aItems) {			 
		System.out.println("  Word      Decimal value   Digital root");
		System.out.println("----------------------------------------");
		for ( Item item : aItems ) {
		    System.out.println(String.format("%7s%15d%12d", item.aWord, item.aNumber, item.aDigitalRoot));
		}
		System.out.println(System.lineSeparator() + "Total count: " + aItems.size() + System.lineSeparator());
	}	
	
	private static record Item(String aWord, int aNumber, int aDigitalRoot) {
				
		public String getWord() { return aWord; }
		
		public int getNumber() { return aNumber; }
		
		public int getDigitalRoot() { return aDigitalRoot; }
		
	}

}
Output:
  Word      Decimal value   Digital 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

Total count: 26

  Word      Decimal value   Digital 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

Total count: 13

jq

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

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.

MiniScript

This implementation is for use with the Mini Micro version of MiniScript. The command-line version does not include a HTTP library. Modify the declaration of wordList object to use the file class instead of the http class. The script already includes this line; just change which line is commented out and ensure the dictionary file is on the local filesystem.

pad = function(n, width, rightJustify = false)
	if rightJustify then
		s = (" " * width + n)[-width:]
	else
		s = (n + " " * width)[:width]
	end if 
	return s
end function

getDigitalRoot = function(n)
	while floor(log(n)) > 0
		sum = 0
		while n > 0
			sum += n % 10
			n = floor(n / 10)
		end while
		n = sum
	end while
	return sum
end function

hexToDec = function(hex)
	digits = "0123456789abcdef"
	result = digits.indexOf(hex[0])
	
	for hdigit in hex[1:]
		result *= 16
		result += digits.indexOf(hdigit)
	end for
	return result
end function

isHexWord = function(word)
	for ch in word.split("")
		if "abcdef".indexOf(ch) == null then return false
	end for
	return true
end function

distinctLetters = function(word)
	letters = {}
	for ch in word.split("")
		letters[ch] = 1
	end for
	return letters.indexes
end function

wordList = http.get("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt").split(char(10))
//wordList = file.readLines("unixdict.txt")

hexWords = []
for word in wordList
	if word.len > 3 and isHexWord(word) then hexWords.push word
end for

roots = []
for hex in hexWords
	decimal = hexToDec(hex)
	root = getDigitalRoot(decimal)
	roots.push [root, hex, decimal]
end for
roots.sort(0)

print "Hex words in unixdict.txt:"
print pad("Root", 6) + pad("Word",10) + "Base 10"
print "-" * 23
for root in roots
	print pad(root[0],6) + pad(root[1],7) + pad(root[2],9,true)
end for
print "Total count of words: " + roots.len

cnt = 0
print
print "Hext words with > 3 distinct letters:"
print pad("Root", 6) + pad("Word",10) + "Base 10"
print "-" * 23
for root in roots
	if distinctLetters(root[1]).len > 3 then
		cnt += 1
		print pad(root[0],6) + pad(root[1],7) + pad(root[2],9,true)
	end if
end for
print "Total count of these words: " + cnt
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 words: 26

Hext 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 these words: 13

Lua

-- Import http namespace from socket library
http = require("socket.http")

-- Download the page at url and return as string
function getFromWeb (url)
    local body, statusCode, headers, statusText = http.request(url)
    if statusCode == 200 then
        return body
    else
        error(statusText)
    end
end

-- Return a boolean to show whether word is a hexword
function isHexWord (word)
    local hexLetters, ch = "abcdef"
    for pos = 1, #word do
        ch = word:sub(pos, pos)
        if not string.find(hexLetters, ch) then return false end
    end
    return true
end

-- Return the sum of the digits in num
function sumDigits (num)
    local sum, nStr, digit = 0, tostring(num)
    for pos = 1, #nStr do
        digit = tonumber(nStr:sub(pos, pos))
        sum = sum + digit
    end
    return sum
end

-- Return the digital root of x
function digitalRoot (x)
    while x > 9 do
        x = sumDigits(x)
    end
    return x
end

-- Return a table from built from the lines of the string dct
-- Each table entry contains the digital root, word and base 10 conversion
function buildTable (dct)
    local t, base10 = {}
    for line in dct:gmatch("[^\n]+") do
        if # line > 3 and isHexWord(line) then
            base10 = (tonumber(line, 16))
            table.insert(t, {digitalRoot(base10), line, base10})
        end
    end
    table.sort(t, function (a,b) return a[1] < b[1] end)
    return t
end

-- Return a boolean to show whether str has at least 4 distinct characters
function fourDistinct (str)
    local distinct, ch = ""
    for pos = 1, #str do
        ch = str:sub(pos, pos)
        if not string.match(distinct, ch) then
            distinct = distinct .. ch
        end
    end
    return #distinct > 3
end

-- Unpack each entry in t and print to the screen
function showTable (t)
    print("\n\nRoot\tWord\tBase 10")
    print("====\t====\t=======")
    for i, v in ipairs(t) do
        print(unpack(v))
    end
    print("\nTable length: " .. #t)
end

-- Main procedure
local dict = getFromWeb("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt")
local hexWords = buildTable(dict)
showTable(hexWords)
local hexWords2 = {}
for k, v in pairs(hexWords) do
    if fourDistinct(v[2]) then
        table.insert(hexWords2, v)
    end
end
table.sort(hexWords2, function (a, b) return a[3] > b[3] end)
showTable(hexWords2)
Output:

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

Table length: 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

Table length: 13

Nim

import std/[algorithm, setutils, strformat, strutils]

const LowerHexDigits = {'a'..'f'}

type Item = tuple[word: string; value, droot: int]

iterator digits(n: Natural): Natural =
  ## Yield the digits of a natural.
  var n = n
  while true:
    yield n mod 10
    n = n div 10
    if n == 0:
      break

func digitalRoot(n: Natural): Natural =
  ## Return the digital root of a natural.
  var n = n
  while true:
    result = 0
    for d in n.digits:
      result += d
    if result <= 9:
      break
    n = result

proc display(items: seq[Item]) =
  ## Display the items.
  echo "Word     Decimal value   Digital root"
  echo repeat("─", 39)
  for item in items:
    echo &"{item.word:<8}{item.value:>12}{item.droot:12}"
  echo "\nTotal count: ", items.len

# Build the list of items.
var items: seq[Item]
for word in lines("unixdict.txt"):
  if word.len >= 4 and card(word.toSet - LowerHexDigits) == 0:
    let val = word.parseHexInt()
    items.add (word, val, val.digitalRoot)

# Sort the items by increasing digital root and display the result.
echo "Hex words in “unixdict.txt”:\n"
items = items.sortedByIt(it.droot)
items.display()

# Remove the items with less than 4 distinct letters.
for i in countdown(items.high, 0):
  if card(items[i].word.toSet) < 4:
    items.delete i

# Sort the items by decreasing value and display the result.
echo "\n\nHex words with more than three distinct letters:\n"
items = items.sortedByIt(-it.value)
items.display()
Output:
Hex words in “unixdict.txt”:

Word     Decimal value   Digital 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

Total count: 26


Hex words with more than three distinct letters:

Word     Decimal value   Digital 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

Total count: 13

Nu

Works with: Nushell version 0.97.1
def make-row [n] {
  {word: $in decimal: $n dr: (($n - 1) mod 9 + 1)}
}

def fmt-table [] {
  $"($in | table -i false -t compact)total count: ($in | length)\n"
}

open 'unixdict.txt' | split words -l 4
| where $it =~ '(?i)^[A-F]+$'
| each { make-row ($in | into int -r 16) }
| [
    ($in | sort-by dr)
    ($in | where ($it.word | split chars | uniq | length) > 3 | sort-by -r decimal)
  ]
| each { fmt-table } | str join "\n"
Output:
────────┬──────────┬────
  word  │ decimal  │ dr 
────────┼──────────┼────
 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

────────┬──────────┬────
  word  │ decimal  │ dr 
────────┼──────────┼────
 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 
────────┴──────────┴────
total count: 13

OmniMark

global integer word-root variable
global integer word-dec variable
global stream dec-word variable

define integer function digroot (value stream vs) as
  local integer d initial {vs base 16}
  local integer r initial {0}
  set new word-dec key vs to d
  repeat
    repeat scan '%d(d)'
      match digit => add
        set r to r + add
    again
    exit when r < 10
    set d to r
    set r to 0
  again
  return r

define switch function isword4-bool (value stream vs) as
  local stream s variable
  repeat scan vs
    match letter => char
      set new s key char to char unless s has key char
  again
  return true when number of s > 3
  return false

process
  submit file 'unixdict.txt'

find line-start (['a' to 'f']{4}['a' to 'f']*) => word line-end
  set new word-root key word to digroot(word)

find any

process-end
  local integer c initial {1}
  output '====== REPORT 1 ======%n' ||
  'Hex words with:%n(1) 4+ letters%n(2) Ascending (digit root)%n' ||
  '======================%nRoot  Word    Decimal%n----------------------%n'
  repeat
    exit when c = 10
    repeat over word-root
      do when word-root = c
        output '%6fd(word-root)' || '8fg' % key of word-root ||
               'd' % word-dec key (key of word-root) || '%n'
      done
    again
    increment c
  again
  set c to number of word-root
  output '%nTotal word count: ' || 'd' % c || '%n'
  output '%n%n====== REPORT 2 ======%n' ||
  'Hex words with:%n(1) 4+ distinct letters%n(2) Descending (decimal)%n' ||
  '======================%nDecimal   Word    Root%n----------------------%n'
  set c to 1
  repeat over word-dec
    do when number of dec-word = 0
      set new dec-word key '%d(word-dec)' to key of word-dec
    else when number of dec-word = 1
      do when word-dec < key of dec-word[1]
        set new dec-word key '%d(word-dec)' after [1] to key of word-dec
      else
        set new dec-word key '%d(word-dec)' before [1] to key of word-dec
      done
    else when number of dec-word > 1
      repeat over dec-word
        do when word-dec < key of dec-word
          increment c
        else
          exit
        done
      again
      set new dec-word key '%d(word-dec)' before [c] to key of word-dec
      set c to 1
    done
  again
  set c to 0
  repeat over dec-word
    do when isword4-bool(dec-word)
      increment c
      output '10fd' % key of dec-word || '%8fg(dec-word)' ||
             'd' % word-root key dec-word || '%n'
    done
  again
  output '%nTotal word count: ' || 'd' % c || '%n'
Output:
====== REPORT 1 ======
Hex words with:
(1) 4+ letters
(2) Ascending (digit root)
======================
Root  Word    Decimal
----------------------
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 word count: 26


====== REPORT 2 ======
Hex words with:
(1) 4+ distinct letters
(2) Descending (decimal)
======================
Decimal   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

Total word count: 13

Pascal

Free Pascal

{$mode ObjFPC}{$H+}
uses
  strutils, classes, sysutils;

const
  FNAME = 'unixdict.txt';

type
  PRec = ^TRec;
  TRec = record
    Root: Uint32;
    Base10: UInt32;
    Hex: String;
  end;

  TRecList = TList;

function DigitalRoot(n: UInt32): UInt32;
{returns the digital root}
begin
  if n < 10 then
    Result := n
  else
    Result := DigitalRoot(n div 10 + n mod 10);
end;

function IsHexWord(const str: string): Boolean;
{returns TRUE if string is a hexword}
var
  ch: Char;
begin
  for ch in str do
    if not (ch in ['a', 'b', 'c', 'd', 'e', 'f']) then
      Exit(FALSE);
  Result := TRUE;
end;

function Has4Distinctive(const str: string): Boolean;
{returns TRUE if string contains 4 or more distinctive charachters}
var
  arr: array['a'..'f'] of Boolean;
  ch: Char;
  counter: Integer;
begin
  for ch := 'a' to 'f' do
    arr[ch] := FALSE;
  counter := 0;
  for ch in str do
    if not arr[ch] then
    begin
      arr[ch] := TRUE;
      Inc(counter);
      if counter = 4 then
        Exit(TRUE);
    end;
  Result := FALSE;
end;

procedure PurgeRecList(var list: TRecList);
{remove every record that doesn have atleast 4 distinctive charachters}
var
  rec: PRec;
  i: Integer;
begin
  for i := Pred(list.Count) downto 0 do
  begin
    rec := list[i];
    if not Has4Distinctive(rec^.Hex) then
      list.Delete(i);
  end;
end;

procedure CreateRecList(var reclist: TRecList; list: TStringList);
{create list of records that have 4 or more charachters and are hexwords}
var
  str: string;
  aPrec: PRec;
begin
  for str in list do
    if (Length(str) > 3) and IsHexWord(str) then
    begin
      New(aPrec);
      aPrec^.Base10 := Hex2Dec(str);
      aPrec^.Root := DigitalRoot(aPrec^.Base10);
      aPrec^.Hex := str;
      reclist.Add(aPrec);
    end;
end;

function SortOnRoot(Item1, Item2: Pointer): Integer;
{sort the list on Root}
begin
  Result := PRec(Item1)^.Root - PRec(Item2)^.Root;
end;

function SortOnBase10(Item1, Item2: Pointer): Integer;
{sort the list on Base 10}
begin
  Result := PRec(Item2)^.Base10 - PRec(Item1)^.Base10;
end;

procedure PrintList(list: TRecList);
var
  rec: PRec;
begin
  Writeln('Root':4, 'Base 10':10, 'Hex Word':10);
  for rec in list do
    Writeln(rec^.Root:4, rec^.Base10:10, rec^.Hex:10);
  Writeln('Total Count:', list.Count);
  Writeln;
end;

var
  list: TStringList;
  RecList: TRecList;

begin
  list := TStringList.Create;
  list.LoadFromFile(FNAME);
  RecList := TRecList.Create;
  CreateRecList(RecList, list); {create list of records purging first set} 
  list.Free;  					{no longer need for the dictionary}
  RecList.Sort(@SortOnRoot); 	{sort list on the root}
  PrintList(RecList); 			{print the list}
  PurgeRecList(RecList); 		{purge list second set}
  RecList.Sort(@SortOnBase10); 	{sort on base 10}
  PrintList(RecList); 			{print the list}
  RecList.Free; 				{free the memory}
end.
Output:
Root   Base 10  Hex Word
   1  14600926    decade
   1     56026      dada
   1     57007      deaf
   1    703162     ababa
   1     43966      abbe
   2     65261      feed
   2     52958      cede
   3    712173     added
   3     44013      abed
   3     47838      bade
   4    782014     beebe
   4    912586     decca
   5     56030      dade
   6     48813      bead
   6  14613198    deface
   7     64222      fade
   7     47806      babe
   8  15727310    efface
   8     57005      dead
   8  16435934    facade
   9     64206      face
   9     48879      beef
   9  11325150    accede
   9     51966      cafe
   9     57069      deed
   9    896202     dacca
Total Count:26

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

Perl

#!/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

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

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

  [ 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

Sorted by digital root with a secondary alphabetical sort.

sub dr (Int $_ is copy) { $_ = .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

Ring

Author = "Gál Zsolt (CalmoSoft)"

load "stdlib.ring"
Words = []
HexWords = ["a","b","c","d","e","f"]
cstr = read("unixdict.txt")
Unix = str2list(cstr)
Unix2 = []
for n = 1 to len(Unix)
    uStr = Unix[n]
    for m = 1 to len(uStr)
        flag =1
        ind = find(HexWords,uStr[m])     
        if ind = 0
           flag = 0
           exit
        ok
     next
     if flag = 1 and len(Unix[n]) > 3
        add(Words,Unix[n])
        add(Unix2,Unix[n])
     ok
next

Unix1 = newlist(len(Words),2)
for n = 1 to len(Words)
    num = dec(Words[n])
    dr = digRoot(num)
    Unix1[n][1] = dr
    Unix1[n][2] = Words[n]
next

Unix1 = sortFirstSecondStr(Unix1,1)

see "Root" + space(2) + "Word" + space(5) + "Base 10" + nl
see "====" + space(2) + "====" + space(5) + "=======" + nl
for n = 1 to len(Unix1)
    decnr = dec(Unix1[n][2])
    see string(Unix1[n][1]) + space(5) + Unix1[n][2] + space(9-len(Unix1[n][2])) + decnr + nl
next

see nl + "Table length: " + len(Unix1) + nl + nl + nl

see "Root" + space(2) + "Word" + space(5) + "Base 10" + nl
see "====" + space(2) + "====" + space(5) + "=======" + nl

for n = 1 to len(Unix2)
    str = Unix2[n]
    str2 = sortStr(str)
    flag = 0
    for p = 1 to len(str2)-1
        st1 = substr(str2,p,1)
        st2 = substr(str2,p+1,1)
        if dec(st1) < dec(st2)
           flag += 1
        ok
    next
    if flag < 4  
       del(Unix2,n)
    ok
next

DecSort = []
for n = 1 to len(Unix2)
    ds = dec(Unix2[n])
    add(DecSort,ds)
next
DecSort = sort(DecSort)
DecSort = reverse(DecSort)

for n = 1 to len(DecSort)
    root = digRoot(DecSort[n])
    word = hex(DecSort[n])
    decnum = DecSort[n]
    see "" + root + space(5) + word + space(9-len(word)) + decnum + nl
next

see nl + "Table length: " + len(DecSort) + nl

func digRoot(num2)
     while true
           strnr = string(num2)
           sum = 0
           for n = 1 to len(strnr)
               sum += number(strnr[n])
           next
           if sum < 10
              exit
           else
              num2 = sum
           ok
     end 
return sum

func sortStr(str2)
     for p = 1 to len(str2)
         for q = p+1 to len(str2)
             if strcmp(str2[q],str2[p]) < 0
                temp = str2[p]
                str2[p] = str2[q]
                str2[q] = temp
             ok
         next
     next
     return str2  

func sortFirstSecondStr(aList,ind)
     aList = sort(aList,ind)
     if (ind = 1)
        nr = 2
     else 
        nr = 1
     ok
     for n=1 to len(alist)-1
	 for m=n+1 to len(aList)
	     if (alist[n][ind] = alist[m][ind]) and
                (strcmp(alist[m][nr],alist[n][nr]) < 0)
		temp = alist[m]
		alist[m] = alist[n]
		alist[n] = temp
             ok
         next
     next
     return aList
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

Table length: 26


Root     Word       Base 10
====  ====     ======
8         facade     16435934
6         deface     14613198
1         decade    14600926
9         accede    11325150
7         fade             64222
9         deed            57069
8         dead            57005
1         dada            56026
2         cede            52958
9         beef            48879
6         bead           48813
7         babe           47806
1         abbe           43966

Table length: 13

RPL

≪ → words 
  ≪ { }
     1 words SIZE FOR w 
       words w GET 
        IF DUP SIZE 4 ≥ THEN 
           "abcdef" 1 SF
           1 3 PICK SIZE FOR c 
              DUP2 SWAP c DUP SUB 
              IF POS NOT THEN 1 CF 99 'c' STO END 
           NEXT DROP
           IF 1 FS? THEN + ELSE DROP END
         ELSE DROP END 
     NEXT 
≫ ≫ 'HXWORDS' STO

≪ 4 UnixDict HXWORDS 0 0
  → diff hexwords value maxroot
   ≪ { }
      1 hexwords SIZE FOR h
        hexwords h GET
        "" 0 'value' STO
        1 3 PICK SIZE FOR c
           OVER c DUP SUB 
           IF DUP2 POS NOT THEN SWAP OVER + SWAP END
           NUM 87 - value 16 * + 'value' STO
        NEXT
        IF SIZE diff < THEN DROP
        ELSE
           value 1 - 9 MOD 1 +
           DUP maxroot MAX 'maxroot' STO
           SWAP value
           3 →LIST 1 →LIST +
        END
     NEXT
     'hexwords' STO { }
      1 maxroot FOR r
        1 hexwords SIZE FOR h
        hexwords h GET
        IF DUP 1 GET r == THEN 1 →LIST + ELSE DROP END
     NEXT NEXT
≫ ≫ 'TASK' STO
Output:
1: { { 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 } }

Ruby

def report(a)
  puts
  a.each {|hexword| puts "%6s  %8d  %d" % hexword}
  puts "Total count of these words: #{a.size}"
end

hexwords = File.readlines("unixdict.txt", chomp: true).reject{|w| w.size < 4 || w.match?(/[^abcdef]/) }
res = hexwords.map{|hw| [hw, hw.to_i(16), 1 + (hw.to_i(16) - 1) % 9]}.sort_by(&:last)
report( res )
report( res.reject{|hw| hw[0].chars.uniq.size < 4}.sort_by{|w| -w[1]} )
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 of these words: 26

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
Total count of these words: 13

V (Vlang)

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

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.

XPL0

include xpllib;         \for Sort

func DigRoot(N);        \Return digital root of N
int  N, S;
loop    [S:= 0;
        repeat  N:= N/10;
        S:= S + rem(0);
        until   N = 0;
        if S < 10 then return S;
        N:= S;
        ];

int     Pass, I, J, K, Ch, Set, Distinct, Values(1000), N, DR;
char    Word(25);
[FSet(FOpen("unixdict.txt", 0), ^I);
for Pass:= 1 to 2 do
        [OpenI(3);
        J:= 0;
        repeat  I:= 0;  Set:= 0;  Distinct:= 0;
                loop    [repeat Ch:= ChIn(3) until Ch # CR; \remove possible CR
                        if Ch=LF or Ch=EOF then
                                [if Pass=1 & I>=4 ! Pass=2 & Distinct>=4 then
                                        [Word(I):= 0;   \terminate string
                                        OpenO(8);
                                        Text(8, Word);
                                        Values(J):= HexIn(8);
                                        J:= J+1;
                                        ];
                                quit;
                                ];
                        if Ch<^a or Ch>^f then          \reject non-hex word
                                [repeat Ch:= ChIn(3) until Ch=LF or Ch=EOF;
                                quit;
                                ];
                        if (Set & 1<<(Ch-^a)) = 0 then Distinct:= Distinct+1;
                        Set:= Set ! 1<<(Ch-^a);
                        Word(I):= Ch;                   \collect hex digits
                        I:= I+1;
                        ];
        until   Ch = EOF;

        if Pass = 2 then Sort(Values, J);
        SetHexDigits(4);
        for N:= 1 to 9 do                               \sort by digital root
                [for K:= J-1 downto 0 do
                        [DR:= DigRoot(Values(K));
                        if Pass=1 & DR=N ! Pass=2 & N=1 then
                                [IntOut(0, DR);  ChOut(0, Tab);
                                HexOut(0, Values(K));  ChOut(0, Tab);
                                IntOut(0, Values(K));  CrLf(0);
                                ];
                        ];
                ];
        Text(0, "Total count: ");  IntOut(0, J);  CrLf(0);
        CrLf(0);
        ];
]
Output:
1       DECADE  14600926
1       DEAF    57007
1       DADA    56026
1       ABBE    43966
1       ABABA   703162
2       FEED    65261
2       CEDE    52958
3       BADE    47838
3       ADDED   712173
3       ABED    44013
4       DECCA   912586
4       BEEBE   782014
5       DADE    56030
6       DEFACE  14613198
6       BEAD    48813
7       FADE    64222
7       BABE    47806
8       FACADE  16435934
8       EFFACE  15727310
8       DEAD    57005
9       FACE    64206
9       DEED    57069
9       DACCA   896202
9       CAFE    51966
9       BEEF    48879
9       ACCEDE  11325150
Total count: 26

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