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:
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.
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
Sounds like you've already gone pretty far down the whole String/memory path, but one last thing to try:
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.
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?
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!