Array property can't be iterated through

I'm trying to iterate through an array property (this.props.items) in a binding transform. For some reason, whether I use the .get() method or brackets to search through the array, I get an error message saying that my key/index has the wrong type.

Specifically: when I try to use a string as the index, it asks for an int, and when I try to use an int as an index, it asks for a string.


Why is this happening? I don't recall ever getting this error in the past when iterating through JsonifiableArrayLists.

Post code - not pictures of code! (Use screengrabs as well if context is useful.) Please see Wiki - how to post code on this forum.

1 Like

Datasource (the value parameter in the transform functions below):

[
  {
    "label": "GPA",
    "expanded": true,
    "icon": {
      "path": "material/business",
      "color": "",
      "style": {}
    },
    "data": {
      "EqID": 1,
      "EqType": "enterprise",
      "EqEnabled": true,
      "EqName": "GPA",
      "EqPath": "GPA"
    },
    "items": [
      {
        "label": "Virginia",
        "expanded": true,
        "icon": {
          "path": "material/location_city",
          "color": "",
          "style": {}
        },
        "data": {
          "EqID": 1,
          "EqType": "site",
          "EqEnabled": true,
          "EqName": "Virginia",
          "EqPath": "GPA/Virginia"
        },
        "items": [
          {
            "label": "Mixing",
            "expanded": true,
            "icon": {
              "path": "material/business_center",
              "color": "",
              "style": {}
            },
            "data": {
              "EqID": 1,
              "EqType": "area",
              "EqEnabled": true,
              "EqName": "Mixing",
              "EqPath": "GPA/Virginia/Mixing"
            },
            "items": [
              {
                "label": "Mixer 1",
                "expanded": false,
                "icon": {
                  "path": "material/line_style",
                  "color": "",
                  "style": {}
                },
                "data": {
                  "EqID": 1,
                  "EqType": "line",
                  "EqEnabled": true,
                  "EqName": "Mixer 1",
                  "EqPath": "GPA/Virginia/Mixing/Mixer 1"
                },
                "items": [
                  {
                    "label": "Drum",
                    "expanded": false,
                    "icon": {
                      "path": "material/apartment",
                      "color": "",
                      "style": {}
                    },
                    "data": {
                      "EqID": 1,
                      "EqType": "cell",
                      "EqEnabled": true,
                      "EqName": "Drum",
                      "EqPath": "GPA/Virginia/Mixing/Mixer 1/Drum"
                    },
                    "items": []
                  },
                  {
                    "label": "Sweeper",
                    "expanded": false,
                    "icon": {
                      "path": "material/apartment",
                      "color": "",
                      "style": {}
                    },
                    "data": {
                      "EqID": 3,
                      "EqType": "cell",
                      "EqEnabled": true,
                      "EqName": "Sweeper",
                      "EqPath": "GPA/Virginia/Mixing/Mixer 1/Sweeper"
                    },
                    "items": []
                  }
                ]
              },
              {
                "label": "Mixer 2",
                "expanded": false,
                "icon": {
                  "path": "material/line_style",
                  "color": "",
                  "style": {}
                },
                "data": {
                  "EqID": 5,
                  "EqType": "line",
                  "EqEnabled": true,
                  "EqName": "Mixer 2",
                  "EqPath": "GPA/Virginia/Mixing/Mixer 2"
                },
                "items": [
                  {
                    "label": "Drum",
                    "expanded": false,
                    "icon": {
                      "path": "material/apartment",
                      "color": "",
                      "style": {}
                    },
                    "data": {
                      "EqID": 4,
                      "EqType": "cell",
                      "EqEnabled": true,
                      "EqName": "Drum",
                      "EqPath": "GPA/Virginia/Mixing/Mixer 2/Drum"
                    },
                    "items": []
                  }
                ]
              },
              {
                "label": "Mixer 3",
                "expanded": false,
                "icon": {
                  "path": "material/line_style",
                  "color": "",
                  "style": {}
                },
                "data": {
                  "EqID": 8,
                  "EqType": "line",
                  "EqEnabled": true,
                  "EqName": "Mixer 3",
                  "EqPath": "GPA/Virginia/Mixing/Mixer 3"
                },
                "items": []
              },
              {
                "label": "Mixer 4",
                "expanded": false,
                "icon": {
                  "path": "material/line_style",
                  "color": "",
                  "style": {}
                },
                "data": {
                  "EqID": 9,
                  "EqType": "line",
                  "EqEnabled": true,
                  "EqName": "Mixer 4",
                  "EqPath": "GPA/Virginia/Mixing/Mixer 4"
                },
                "items": [
                  {
                    "label": "Cell 1",
                    "expanded": false,
                    "icon": {
                      "path": "material/apartment",
                      "color": "",
                      "style": {}
                    },
                    "data": {
                      "EqID": 10,
                      "EqType": "cell",
                      "EqEnabled": true,
                      "EqName": "Cell 1",
                      "EqPath": "GPA/Virginia/Mixing/Mixer 4/Cell 1"
                    },
                    "items": []
                  },
                  {
                    "label": "Cell 2",
                    "expanded": false,
                    "icon": {
                      "path": "material/apartment",
                      "color": "",
                      "style": {}
                    },
                    "data": {
                      "EqID": 11,
                      "EqType": "cell",
                      "EqEnabled": true,
                      "EqName": "Cell 2",
                      "EqPath": "GPA/Virginia/Mixing/Mixer 4/Cell 2"
                    },
                    "items": []
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

When I try to pass index as a string:

def transform(self, value, quality, timestamp):
	final_node = {
		"EqEnabled": "",
		"EqID": "",
		"EqName": "",
		"EqPath": "",
		"EqType": ""
	}
	
	if self.props.selection[0] and len(self.props.selection) > 0: 
		path = self.props.selection[0].split("/") # 0/0/0/0
		current_node = value
		current_node_index = 0
		path_array_final_index = len(path) - 1
		for index in path:
			index = index
			current_node = current_node.get(index)
			current_node_index += 1
			if current_node_index != path_array_final_index:
				current_node = current_node.get("items")
		
		current_node_data = current_node["data"]
		final_node["EqEnabled"] = current_node_data["EqEnabled"]
		final_node["EqID"] = current_node_data["EqID"]
		final_node["EqName"] = current_node_data["EqName"]
		final_node["EqPath"] = current_node_data["EqPath"]
		final_node["EqType"] = current_node_data["EqType"]
	
	return final_node
Traceback (most recent call last): File "<transform>", line 16, in transform TypeError: com.inductiveautomation.perspective.gateway.script.JsonifiableArrayList indices must be integers

When I try to pass index as an integer:

def transform(self, value, quality, timestamp):
	final_node = {
		"EqEnabled": "",
		"EqID": "",
		"EqName": "",
		"EqPath": "",
		"EqType": ""
	}
	
	if self.props.selection[0] and len(self.props.selection) > 0: 
		path = self.props.selection[0].split("/") # 0/0/0/0
		current_node = value
		current_node_index = 0
		path_array_final_index = len(path) - 1
		for index in path:
			index = index
			current_node = current_node.get(int(index))
			current_node_index += 1
			if current_node_index != path_array_final_index:
				current_node = current_node.get("items")
		
		current_node_data = current_node["data"]
		final_node["EqEnabled"] = current_node_data["EqEnabled"]
		final_node["EqID"] = current_node_data["EqID"]
		final_node["EqName"] = current_node_data["EqName"]
		final_node["EqPath"] = current_node_data["EqPath"]
		final_node["EqType"] = current_node_data["EqType"]
	
	return final_node
Traceback (most recent call last): File "<transform>", line 17, in transform TypeError: expected a str

Sometimes current_node is an object, and sometimes it's an array. So whichever indexing strategy you attempt isn't valid, some of the time.

1 Like

But current_node in current_node = current_node.get(int(index)) is always going to be the array type in every instance that the line of code is called. So ideally, there should be a method of indexing arrays that always works for this line of code.

item = {'items': value}
for n in self.props.selection.data.split('/'):
    item = item['items'][int(n)]

But... What exactly are you trying to do ? The whole thing looks quite weird to me...

Looks like they are trying to traverse a tree structure. Some of my scripts to get the selected item out of a tree component looked similar when I first started. selectedData has some weird behavior when you are updating the tree structure.

1 Like

For your purpose, looks like using self.props.selection.data would be enough like @pascal.fragnoud suggested. Though, if you still want to traverse the items tree and also grab all its children's data, you can use something like this

indices = [int(x) for x in self.props.selection[0].split("/")]
items = self.props.items
for i in indices:
	item = items[i]
	items = item.items

# at this point, you can extract whatever you want from item

credit to @pturmel, this is something I learned from his TagReportUtility exchange resource

1 Like

No, it starts off as a list, then inside the for it become a dict, then the second for iteration will fail because the current node is now a dict and you can't supply in int index

You should definitely avoid reusing the same variable and morphing it into whatever you want, as it's almost impossible to not be caught by it, case in point

4 Likes

I feel like the naming is to blame here.
The variable is named current_node, but it doesn't actually start as the current node. It starts as a list of available nodes for this level.
This is most likely the source of the confusion.

I can't remember who said that naming things is the hardest thing in programming, but he was partly right. Good names are precious, they make everything else easier. They force you to think hard about what you're doing, and that in itself is a pain and a blessing.

1 Like

That and locating things in a logical spot... I always find perspective styles incredibly hard to name and locate