Getting a list of unique values from a field

5157
6
05-28-2015 09:03 PM
Justin_ODell
New Contributor III

I'm trying to retrieve a list of unique values from a particular field in a layer using ArcObjects.

I was using the IDataStatistics.UniqueValues, but it causes the results to be corrupted as soon as the function is exited (in ArcMap 10.2+).  I think it's related to some longstanding issues with IDataStatistics (Issues with ArcObjects and ArcGIS 10.1 SP1 IDataStatistics  as an example).

I tried using the DISTINCT prefix on the IQueryFilterDefinition2, per the end of the discussion on the last thread, but couldn't get it to work on a shapefile -- It did work on the same data in a File Geodatabase.  What are the limitations of IQueryFilterDefinition2?  Have any other suggestions to get a list of unique values quickly?

Some Caveats:

  • It's for commercial software and needs to support ArcMap 10.0-10.3.X
  • We work with all flavors of GeoFeatureLayers and need to support shp, pgdb, fgdb, sde, etc.
  • This function needs to run very quickly, because it's called every time the map refreshes (invalidates) and may be retrieving unique values from one column, from multiple layers with a large number of features each
0 Kudos
6 Replies
ErinBrimhall
Occasional Contributor II

Hi Justin,

With regard to IDataStatistics.UniqueValues being corrupted, were you simply trying to return the value of this property (an IEnumerator instance) from your function?

Since IDataStatistics functionality is cursor-based, it's not entirely surprising that the contents of UniqueValues becomes corrupt once your function completes and the objects are out of scope.  You should consider copying the contents of the UniqueValues property to a separate variable (array, IList, or similar) and returning that from your function.

0 Kudos
Justin_ODell
New Contributor III

Thanks Erin!

No -- I was trying to return the values as an array of strings.  Here is a sample with the gist of my code:

Public Function getCentersFromLayer(... sCenters() As String, ...) As Long
    ...
    Dim pDataStatistics As IDataStatistics
    Set pDataStatistics = New DataStatistics
    pDataStatistics.Field = FIELD_NAME
    Set pDataStatistics.Cursor = pFeatureCursor

    Dim pEnumVar As IEnumVariantSimple
    Set pEnumVar = pDataStatistics.UniqueValues
    ...
    Dim value As Variant
    value = pEnumVar.Next

    Do Until isEmpty(value)
        sCenters(getCentersFromLayer) = CStr(value)
        value = pEnumVar.Next
    Loop

End Function

sCenters  (and the string array passed into it) had the correct values up until after control returned on line 19.  I even tried using the CopyString (ByVal) function below to replace the CStr() on line 15, to ensure I didn't have references to the original memory.

Public Function CopyString (ByVal s as String) as String
    CopyString = s
End Function
0 Kudos
ErinBrimhall
Occasional Contributor II

Sounds like you've already gone pretty far down the whole String/memory path, but one last thing to try:

String.Copy

The Copy method returns a String object that has the same value as the original string but represents a different object reference. It differs from an assignment operation, which assigns an existing string reference to an additional object variable."

Assuming that the contents of the UniqueValues property look correct as you iterate through, this problem should be solvable through variable/memory management.

0 Kudos
KenBuja
MVP Esteemed Contributor

I'm not sure I understand your syntax. You're declaring sCenters as a local variable inside the function, so it will be unavailable outside that function. How are you getting the array out of that function?

0 Kudos
Justin_ODell
New Contributor III

Passing it as a reference parameter.  In VBA, all parameters are passed by reference unless specified (eg. ByVal).

An excerpt from my example:

    Dim sOuterList() As String
    getListFromLayer(pDoc.FocusMap.Layer(0), sOuterList)

Sadly, I think using VBA/VB6 also prevents us from using String.Copy.

Thanks!

0 Kudos
ErinBrimhall
Occasional Contributor II

OK, one more shot at string/memory copy in VBA: CopyMemory

A couple references, here and here.

0 Kudos