Is there a practical limit to the number of custom properties on an object

I have a power table, 6x9, 54 cells in Vision.

I need the first columns cell values to act as an Indirect to the remaining 8 cells on that row. As I understand it, Cell update bindings can't resolve nested properties/tags, so I'm reduced to creating a custom property for every cell. Didn't know if 54 custom properties would be ridiculous or if there is a better way to achieve this.

54 is totally fine.
540 should still work fine, but is absolutely "you're doing something wrong" territory.
5400 is probably going to still work okay, but will be essentially unmaintainable.

From a maintainability and overhead standpoint, would it be better to just create a script on property change that dynamically reads the values of tags indirectly and populates the cells for each column based on the column[0] value of each row? Wasn't sure if doing a bunch of Readblocking of tags would be worse than just brute force of 54 properties, bound to indirects. Either way is 54 tag reads.

I would use my Integration Toolkit's tags() function with a some custom dataset properties. The first would be a list of base tags you wish to monitor, possibly dynamically updated. The second would be a three-column dataset containing the suffixes you wish to add to the base tag paths, and the corresponding output column name you wish, and the output column type to use. Likely just a constant in your design. The third custom property would be a flat list of each of those merged with property suffixes, as needed. Its expression binding would look something like this:

unionAll(
	asMap('completePath', 'str'),
	flatten(
		forEach(
			{Root container.baseTagList},
			forEach(
				{Root container.suffixes},
				asList(
					concat(it(1)[0], it()[0])
				)
			)
		)
	)
)

Then the final expression would feed that list to the tags() function, wrapped to yield the N+1 × M output dataset. Something like this:


unionAll(
	asPairs(  // Used to concatenate lists here
		asMap('Tag', 'str'),
		forEach(
			{Root container.suffixes},
			it()[1], // Column name
			it()[2]  // Column type
		)
	),
	forEach(
		groupBy(
			forceQuality(  // So missing props don't break us.
				tags({Root Container.mergedPaths}, 50), // Fifty millis dwell for smooth updates.
				192
			),
			transform(
				lastIndexOf(it()[0], "."),
				if(
					value() >= 0, // If a dot is present, take everything before it.
					left(it()[0], value()),
					it()[0] // Otherwise, use the whole (base) tag path
				)
			)
		), // Yields grouped rows by base tag path
		asPairs(  // Used to concatenate lists here
			asList(it()[0]), // grouped tag path to match the "Tag" column
			transform( // Make a dictionary by property suffixes with tag values
				asMap(
					forEach(
						it()[1], // Dataset of base tag group, paths+values
						transform(
							lastIndexOf(it()[0], "."),
							if(
								value() >= 0, // If a dot is present, take it and after.
								substring(it()[0], value()),
								"" // Otherwise, use an empty string
							)
						),
						it()[1] // Value read.
					)
				),
				forEach( // Look up each suffix to assemble the row
					{Root container.suffixes},
					value()[it()[1]]
				)
			)
		)
	)
)

My tags() function will be much more efficient than any kind of script, as it will subscribe to the actual tags intelligently.

The above looks rough at first glance, but well worth the effort.

https://www.automation-pros.com/toolkit/doc/

Note, include a suffix with an empty string to get the live tag value itself.

Also note: untested.... :grin:

Awesome...I'll give it a whirl!