Select to view content in your preferred language

Create a table with domains as columns

143
3
a week ago
EleanorCervigni
Occasional Contributor

I am trying to use the table widget to build a table that has grouped values for both columns and rows. Eg this example:

 Field 2, domain 1Field 2, domain 2Field 2, domain 3
Field 1, domain 1   
Field 1, domain 2   
Field 1, domain 3   

 

Obviously rows are easy but if i understand correctly i would need to restructure my data so that i have a column for each of the domains in Field 2? I have tried to do this with arcade and get to the point where I have a dictionary but cannot convert it into a featureset to use in the table.. Here is where I am at with the code, but I am getting an error with the push function.

I am new to arcade so am probably making many mistakes, Any help is appreciated!


Thanks

 

```

// Define the field to extract coded domains from
var targetField = "sys_workflow_status"; // Change this to match the actual field name


var fs = FeatureSetByPortalItem(
Portal("portall"), // URL to your Portal or ArcGIS Online
"id", // Replace with your actual layer item ID
0, // Layer index (0 for the first layer)
["*"] // Fields to include (all fields in this case)
);

// Check if the feature set is empty
if (IsEmpty(fs)) {
return "Error: No features found in this dataset.";
}

// Get the schema for the layer to retrieve domain information
var layerSchema = Schema(fs);
var fieldNames = [];

// Loop through the schema fields to extract field names
for (var i = 0; i < Count(layerSchema.fields); i++) {
var field = layerSchema.fields[i];
Push(fieldNames, field.name); // Access the 'name' property of the field object
}

// Check if the target field exists in the list of field names
var fieldIndex = IndexOf(fieldNames, targetField);

// If the field does not exist, return an error
if (fieldIndex == -1) {
return "Error: Field '" + targetField + "' not found in the feature set.";
}

// Retrieve the domain information for the target field
var domainInfo = null;
for (var i = 0; i < Count(layerSchema.fields); i++) {
var field = layerSchema.fields[i];
// Check if the field has a domain (not null or undefined)
if (field.name == targetField && field.domain != null) {
domainInfo = field.domain;
break;
}
}

// If no domain is found, return a message
if (domainInfo == null) {
return "Error: No domain found for field '" + targetField + "'.";
}

// Create a dictionary of domain mappings (assumes it's a coded value domain)
var domainMappings = {};
if (domainInfo.type == "codedValue") {
// If the domain type is coded value, we can extract the coded values and their descriptions
for (var i = 0; i < Count(domainInfo.codedValues); i++) {
var codedValue = domainInfo.codedValues[i];
domainMappings[codedValue.code] = codedValue.name;
}
}

// Create a dictionary to store the modified feature results
var resultDict = {};

// Loop through all the features in the feature set
for (var f in fs) {

// Create a new result object for each feature, starting with the original attributes
var featureResult = {};
var geom = Geometry(f); // Get the geometry from the original feature

// Copy the original fields into the result (to retain the original attributes)
for (var fieldName in f) {
featureResult[fieldName] = f[fieldName];
}

// Get the value for the target field in the current feature
var targetValue = f[targetField];

// Loop through the domain mappings and create a new column for each domain value
for (var domainCod in domainMappings) {
var domainValue = domainMappings[domainCod];

// If the value matches the domain code, set the new column value to 1, otherwise set it to 0
if (targetValue == domainCod) {
featureResult[targetField + "_" + domainValue] = 1;
} else {
featureResult[targetField + "_" + domainValue] = 0;
}
}

// Use a string to store the key (e.g., ObjectID or any unique identifier)
var objectID = Text(f["ObjectID"]);

// Add the modified feature result to the dictionary using the string key (ObjectID)
resultDict[objectID] = featureResult;
}


// Now, convert the dictionary back into a FeatureSet
var resultFeatures = [];
for (var key in resultDict) {
var fData = resultDict[key]; // Changed variable name from 'feature' to 'fData'

// Create a new feature from the geometry and the updated attributes
var newFeature = Feature(geom); // Initialize a new feature with geometry

// Set the fields for the new feature
for (var fieldName in fData) {
// Add the field and its value to the new feature
newFeature[fieldName] = fData[fieldName];
}

// Add the new feature to the result features list
resultFeatures.push(newFeature);
}

// Create a FeatureSet from the result features array
var newFeatureSet = FeatureSet(resultFeatures);

// Return the new FeatureSet for use in the table widget
return newFeatureSet;

 

Tags (2)
0 Kudos
3 Replies
KenBuja
MVP Esteemed Contributor

Can you give a better idea of what you'd like your final table to look like? Are you trying to get a summary of how many records are in the domains for each pair of fields? I'm guessing if your table looks like this

idField 1Field2
1domain 1domain 1
2domain 2domain 1
3domain 3domain 3
4domain 1domain 1
5domain 1domain 2

 

you want your output table looking like this.

 Field 2, domain 1Field 2, domain 2Field 2, domain 3
Field 1, domain 1200
Field 1, domain 2110
Field 1, domain 3001

 

Is this correct?

0 Kudos
EleanorCervigni
Occasional Contributor

Yes thats it, the domains are different in each field. For clarity here is an example with some fake dataö

Original table

IDReport statusSuburb
1submittedperth
2assignedbooragoon
3finalisedbooragoon
4finalisedperth
5assignedarmadale

 

Output table:

 SubmittedAssignedFinalised
Perth101
Booragoon011
Armadale010

 

0 Kudos
KenBuja
MVP Esteemed Contributor

Here's the script I came up with. It does your error checking, returning a table (InvalidTable function) with the error message if the featureset doesn't contain the fields or fields with domains. Since this could be a very memory-intensive code as it uses the Filter function for each combination of domains, I also include @jcarlson's Memorize function to read the FeatureSet into memory.

Note that in my test, this used domains with a numeric code, so I used the description for its name (lines 111 and 121). If you use string codes, you can just use "key" and "ykey" instead of "xDomainMappings[key]" and "yDomainMappings[ykey]" in those lines.

 

function InvalidTable(message) {
  return FeatureSet(
    {
      fields: [{ alias: "error", name: "error", type: "esriFieldTypeString" }],
      geometryType: "",
      features: [{ attributes: { error: message } }]
    }
  );
}

function Memorize(fs) {
  var temp_dict = {
    fields: Schema(fs)["fields"],
    geometryType: "",
    features: []
  };
  for (var f in fs) {
    var attrs = {};
    for (var attr in f) {
      attrs[attr] = Iif(TypeOf(f[attr]) == "Date", Number(f[attr]), f[attr]);
    }
    Push(temp_dict["features"], { attributes: attrs });
  }
  return FeatureSet(temp_dict);
}

// Define the field to extract coded domains from
var targetFields = ["xAxisField", "xAxisField"]; //Change this to match the actual field names
var targetXField = targetFields[0]; 
var targetYField = targetFields[1];

var inputFS = FeatureSetByPortalItem(
  Portal("your portal"),
  "itemId",
  0,
  targetFields
);

// Check if the feature set is empty
if (IsEmpty(inputFS)) {
  return InvalidTable("Error: No features found in this dataset.");
}

// Get the schema for the layer to retrieve domain information
var fields = Schema(inputFS).fields;
var containsXTargetField = false;
var containsYTargetField = false;
var xDomainInfo;
var yDomainInfo;

// Loop through the schema fields to extract field names
for (var i = 0; i < Count(fields); i++) {
  var field = fields[i];
  if (field.name == targetXField) {
    containsXTargetField = true;
    // Retrieve the domain information for the XTarget field
    if (field.domain != null) xDomainInfo = field.domain;
  }
  if (field.name == targetYField) {
    containsYTargetField = true;
    // Retrieve the domain information for the YTarget field
    if (field.domain != null) yDomainInfo = field.domain;
  }
}

// Check if the target field exists in the list of field names
// If the field does not exist, return an error
if (!containsXTargetField)
  return InvalidTable(
    `Error: Field '${targetXField}' not found in the feature set.`
  );
if (!containsYTargetField)
  return InvalidTable(
    `Error: Field '${targetYField}' not found in the feature set.`
  );

// If no domain is found, return a message
if (IsEmpty(xdomainInfo))
  return InvalidTable(`Error: No domain found for field '${targetXField}'.`);
if (IsEmpty(ydomainInfo))
  return InvalidTable(`Error: No domain found for field '${targetYField}'.`);

// Create a dictionary of domain mappings (assumes it's a coded value domain)
var xDomainMappings = {};
if (xDomainInfo.type != "codedValue")
  return InvalidTable(
    `Error: Domain for field '${targetXField}' is not coded-value.`
  );
// If the domain type is coded value, we can extract the coded values and their descriptions
for (var i = 0; i < Count(xDomainInfo.codedValues); i++) {
  var codedValue = xDomainInfo.codedValues[i];
  xDomainMappings[`${codedValue.code}`] = codedValue.name;
}

var yDomainMappings = {};
if (yDomainInfo.type != "codedValue")
  return InvalidTable(
    `Error: Domain for field '${targetYField}' is not coded-value.`
  );
// If the domain type is coded value, we can extract the coded values and their descriptions
for (var i = 0; i < Count(yDomainInfo.codedValues); i++) {
  var codedValue = yDomainInfo.codedValues[i];
  yDomainMappings[`${codedValue.code}`] = codedValue.name;
}
console(xDomainMappings)
//Create the fields to hold the counts
var fieldList = [{ alias: "Domain", name: "Domain", type: "esriFieldTypeString" }];
for (var key in xDomainMappings) {
  Push(
    fieldList,
    { alias: xDomainMappings[key], name: key, type: "esriFieldTypeInteger" }
  );
}

//Store the fs in memory for multiple Filter calls
var fs = Memorize(inputFS);

//Loop through all the combinations of domains
var features = [];
for (var ykey in yDomainMappings) {
  var attributes = { Domain: yDomainMappings[ykey] };
  for (var xkey in xDomainMappings) {
    var query = `${targetXField} = @xkey AND ${targetyField} = @ykey`;
    attributes[xkey] = Count(Filter(fs, query));
    //console(`ykey: ${ykey}, xkey: ${xkey}: ${attributes[xkey]}`);
  }
  Push(features, { attributes: attributes });
}

var dict = { fields: fieldList, features: features };
return FeatureSet(dict);

 

 I ran this on my data and got this table.

Snag_18c4ace.png

I tested these results using this GroupBy function,

 

return GroupBy(fs, targetFields, {name: "Count", expression: '1', statistic: 'Count'})

 

which gave me a table of the counts of each combination that was greater than zero.

Snag_18e5217.png

0 Kudos