Unique values from 1D-array, without iteration

后端 未结 4 721
陌清茗
陌清茗 2021-01-13 19:25

At risk of being of topic, I decided to share some code, Q&A-style. If the general opinion is such that this would be off-topic I\'ll be happy to delete if need be.

4条回答
  •  伪装坚强ぢ
    2021-01-13 19:39

    Approach via FilterXML()

    Just to enrich the variety of fine solutions above, I demonstrate an approach via the new worksheet function FilterXML().

    Sub testUniqueItems()
    '   Purp: list unique items
    '   Site: https://stackoverflow.com/questions/59683363/unique-values-from-1d-array-without-iteration
        Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
    '[1]get uniques
        Dim uniques
        uniques = UniqueXML(arr)
    '[2]display in Immediate Window: A,A,C,D,A,E,G => A,C,D,E,G
        Debug.Print Join(arr, ",") & " => " & _
                    Join(uniques, ",")
    End Sub
    
    Function UniqueXML(arr, Optional Delim As String = ",")
      ' Purp: return unique list of array items
      ' Note: optional argument Delim defaulting to colon (",")
      ' Help: https://docs.microsoft.com/de-de/office/vba/api/excel.worksheetfunction.filterxml
      ' [1] get array data to xml node structure (including root element)
        Dim wellformed As String
        wellformed = "" & Join(arr, "") & ""
      ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ' [2] define XPath string searching unique item values
      ' Note: c.f. udf: https://stackoverflow.com/questions/58677041/vba-excel-how-to-display-non-equal-values-in-an-excel-array/58685756#58685756
      ' ------------------------------------------------
      ' //i                    ... all  node values after the DocumentElement
      ' [not( .=preceding::i)] ... only if not preceded by siblings of the same node value
      ' ------------------------------------------------
        Dim myXPath As String
        myXPath = "//i[not( .=preceding::i)]"
      ' [3a] get (delimiter separated) unique list
        UniqueXML = Evaluate("=TEXTJOIN(""" & Delim & """,,FILTERXML(""" & wellformed & """, """ & myXPath & """))")
      ' [3b] return array
        UniqueXML = Split(UniqueXML, Delim)
    End Function
    
    

    Related links

    MS Help

    Display non equal values in an Excel array

    Caveat

    Note that the Worksheet function FilterXML() can be used from vers. 2016+, but TextJoin only in vers. 2019+ (thx @FaneDuru for comments:-)

    Furthermore you would have to be aware of the limits of evaluate. 255 chars only (thx @JvDv).

    To overcome both obstacles I reworked above function to work in versions 2016+, too.

    Modified function /Edit as of 2020-08-20

    Function UniqueXML(arr, Optional Delim As String = ",")
      ' Purp: return unique list of array items
      ' Note: optional argument Delim defaulting to colon (",")
      ' Help: https://docs.microsoft.com/de-de/office/vba/api/excel.worksheetfunction.filterxml
      ' [1] get array data to xml node structure (including root element)
        Dim wellformed As String
        wellformed = "" & Join(arr, "") & ""
      ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ' [2] define XPath string searching unique item values
      ' Note: c.f. udf: https://stackoverflow.com/questions/58677041/vba-excel-how-to-display-non-equal-values-in-an-excel-array/58685756#58685756
      ' ------------------------------------------------
      ' //i                    ... all  node values after the DocumentElement
      ' [not( .=preceding::i)] ... only if not preceded by siblings of the same node value
      ' ------------------------------------------------
        Dim myXPath As String
        myXPath = "//i[not( .=preceding::i)]"
       
      ' [3] get "flat" 1-dim array (~> one-based!)
        Dim tmp As Variant
        tmp = Application.Transpose(WorksheetFunction.FilterXML(wellformed, myXPath))
    '  ' [3a] optional redim as zero-based array
    '    ReDim Preserve tmp(LBound(tmp) - 1 To UBound(tmp) - 1)
            
      ' [4] return function result
        UniqueXML = tmp
    End Function
    

提交回复
热议问题