Alternative to the newer dynamic array functions
Loving the above nice solutions it's always a challenge to think over additional approaches (via Byte array, Filter() and FilterXML() function):
Function UniqueDigits(ByVal txt) As String
Dim by() As Byte: by = txt
Dim digits: digits = Array(49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 0) ' equalling Asc values
'a) create 1-based 1-dim array with digit positions
Dim tmp: tmp = Filter(Application.Match(by, digits, 0), 11, False)
'b) get uniques
tmp = Uniques(tmp)
'c) sort it (don't execute before getting uniques)
BubbleSort tmp
'd) return function result
UniqueDigits = Join(tmp, "")
End Function
Function Uniques(arr)
'Note: using FilterXML() available since vers. 2013+
Dim content As String ' replacing "10" referring to zero indexed as 10th digit
content = Replace("<t><s>" & Join(arr, "</s><s>") & "</s></t>", "10", "0")
arr = WorksheetFunction.FilterXML(content, "//s[not(preceding::*=.)]")
Uniques = Application.Transpose(arr)
End Function
Sub BubbleSort(arr)
Dim cnt As Long, nxt As Long, temp
For cnt = LBound(arr) To UBound(arr) - 1
For nxt = cnt + 1 To UBound(arr)
If arr(cnt) > arr(nxt) Then
temp = arr(cnt)
arr(cnt) = arr(nxt)
arr(nxt) = temp
End If
Next nxt
Next cnt
End Sub
Further hints :-) tl;dr
...explaining
a) how to transform a string to a digits array,
b) how to get uniques via FilterXML instead of a dictionary
c) (executing BubbleSort needs no further explanation).
ad a) the tricky way to get a pure digits array
Transforming a string of digits into an array of single characters may need some explanation.
- A string (here
txt) can assigned easily to a byte array via Dim by() As Byte: by = txt. (Note that classical characters would be represented in a byte array by a pair of Asc values, where the second value mostly
is 0; so digit 1 is represented by 49 and 0, 2 by 50 and 0 up to 9 by 57 and 0).
Digits are defined in a 1-based Asc value array from 1~>49 to 9~>57, followed by the 10th item 0~>48 and
eventually the Asc value 0 as 11th item related to each second byte pair.
Dim digits: digits = Array(49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 0) ' equalling Asc values
- Usually the
Match() function searches for a specified item in order to get its relative position within an array (here digits) and would be executed by the following syntax: ``.
MATCH(lookup_value, lookup_array, [match_type])
where the lookup_value argument can be a value (number, text, or logical value) or a cell reference to a number, text, or logical value.
An undocumented feature is that instead searching e.g. 2 in the lookup_array digits via
Application.Match(2, digits,0) you can use the byte array as first argument serving as 1-based array pattern where VBA replaces the current Asc values by their position found within the digits array.
Application.Match(by, digits, 0)
Finally a negative filtering removes the companion Asc 0 values (11 plus argument False) via
Dim tmp: tmp = Filter(Application.Match(by, digits, 0), 11, False)
ad b) get uniques via FilterXML
Help reference for the WorksheetFunction.FilterXML method demands two string parameters
FilterXML(XMLContentString, XPathQueryString)
The first argument doesn't reference a file, but needs a valid ("wellformed") XML text string starting with a root node (DocumentElement) which is roughly comparable to a html structure starting with the enclosing pair of <html>...</html> tags.
So a wellformed content string representing e.g. number 121 could be:
<t>
<s>1</s>
<s>2</s>
<s>1</s>
</t>
The second argument (limited to 1024 characters) must be a valid XPath query string like the following find non-duplicates
"//s[not(preceding::*=.)]"
where the double slash // allows to find s nodes at any hierarchy level and under the condition that it is not
preceded by any nodes * with the same value content =.
Recommended readings
@RonRosenfeld is a pioneer author of numerous answers covering the FilterXML method, such as e.g. Split string cell....
@JvDV wrote a nearly encyclopaedic overview at Extract substrings from string using FilterXML.