# Read UDT Instance vs Read Members

**URL:** <https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074>\
**Category:** Ignition\
**Tags:** ignition81\
**Created:** [September 17, 2024, 4:02pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074 "2024-09-17T16:02:05Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 17, 2024, 4:02pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/1 "2024-09-17T16:02:05Z")

</div>

I have a UDT definition with 19 members. I need to read 3 of them. Wondering if it's more efficient to read the 3 I need or if I should read the entire UDT and extract from the dictionary.

Or, what's best practice?

Code is a bit simpler if I extract from the dictionary because I don't have to build the individual member paths and then extract them in parallel.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 17, 2024, 4:15pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/2 "2024-09-17T16:15:01Z")

</div>

Are you reading a LOT of those ?

If not, don't worry about performances, do it in the simplest possible way.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 17, 2024, 4:15pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/3 "2024-09-17T16:15:25Z")

</div>

I'll end up reading all instances of the UDT that exist on the gateway.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [September 17, 2024, 4:15pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/4 "2024-09-17T16:15:41Z")

</div>

> [@amarks](#):
>
> Code is a bit simpler if I extract from the dictionary because I don't have to build the individual member paths and then extract them in parallel.

I'm not sure it really matters all that much, unless you're doing it a number of times. In which case less reads will always be more performant.

That said. This is pretty much a one liner. So i'm not sure what simpler looks like.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 17, 2024, 4:16pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/5 "2024-09-17T16:16:07Z")

</div>

Either way, it's one call to `system.tag.readBlocking()`

Code is much simpler and more readable reading the entire UDT

```python
headers = ['Number','Type','Name']
paths = getUDTtagPathList('Grain_Equipment')

equipment = system.tag.readBlocking(paths)
	
data = []
for equip in equipment:
	data.append([equip.value[eqNumber],equip.value[eqType],equip.value[eqName]])
	
return system.dataset.toDataSet(headers, data)

```

instead of

```python
headers = ['Number','Type','Name']
paths = getUDTtagPathList('Grain_Equipment')

tagsToRead = [str(path)+'/'+suffix for path in paths for suffix in headers]
	
tagsVals = [tag.value for tag in system.tag.readBlocking(tagsToRead)]

data = [tagsVals[i:i+len(headers)] for i in range(0, len(tagsVals), len(headers))]

return system.dataset.toDataSet(headers, data)

```

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [September 17, 2024, 5:12pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/6 "2024-09-17T17:12:44Z")

</div>

I'm not sure what the comprehension for `data` is trying to achieve, your basically going a long way around stepping through each value in tagVals, which you already know will be in groups of the length of headers.

Either way, the dictionary script:

```python
headers = ['Number', 'Type', 'Name']
paths = getUDTtagPathsList('Grain_Equipment')

data = [[equip.value[eqNumber],equip.value[eqType],equip.value[eqName]] for equip in equipment]

return system.dataset.toDataSet(headers,data)

```

For the second one, I would throw this function into your utilities library because it is super handy for things just like this:

```python
def chunker(seq,size):
    return (seq[pos:pos + size] for pos in xrange(0,len(seq),size))

```

Then your second script can be this:

EDIT: replaced zip with product, as @pascal.fragnoud correctly pointed out, zip is not what we want in this instance.

```python
from itertools import product
headers = ['Number', 'Type', 'Name']
paths = ['{}/{}'.format(path,suffix) for path,suffix in product(getUDTtagPathsList('Grain_Equipment'),headers)]

data = [[qv.value for qv in chunk] for chunk in chunker(system.tag.readBlocking(paths),len(headers))]

return system.dataset.toDataSet(headers,data)

```

You may feel that is less readable (particularly for the un-initiated), and that's fair. To me the difference is negligible, but as the UDT list increases the second will be more performant. The only way to be certain is to measure the performance on your system. Either one is a perfectly acceptable way to do what you're doing.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 17, 2024, 5:54pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/7 "2024-09-17T17:54:18Z")

</div>

> [@lrose](#):
>
> ```python
> paths = ['{}/{}'.format(path,suffix) for path,suffix in zip(getUDTtagPathsList('Grain_Equipment'),headers)]
> 
> ```

That won't work, it's only gonna match each udt with one suffix.

`Itertools` provides a simple way to do this (as it often does):

```python
from itertools import product

udts = ['foo', 'bar', 'pox']
headers = ['number', 'type', 'name']
paths = ["{}/{}".format(path, suffix) for path, suffix in product(udts, headers)]

```

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 17, 2024, 6:10pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/8 "2024-09-17T18:10:25Z")

</div>

The data comprehension in the second one was because they would be in the wrong order if stepped through one by one, the way I had constructed the member paths.

Thanks for pointing out the comprehension I missed for the first method.

I'm going to stick with the first method for now. The nice thing about modularity and scripting library is that it's easy to update in the future if it becomes a problem.

---

<div class="post-metadata">

**Author:** ![vtran](https://avatars.discourse-cdn.com/v4/letter/v/779978/32.png) [@vtran](https://forum.inductiveautomation.com/u/vtran)\
**Post date:** [September 17, 2024, 6:17pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/9 "2024-09-17T18:17:12Z")

</div>

Is there any reason to not just use double `for` in the comprehension instead of import from Itertools?

```python
paths = ["{}/{}".format(path, suffix) for path in udts for suffix in headers]

```

It seems to produce the same list, but I'm not sure if there's any performance advantage in using `product`

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [September 17, 2024, 6:21pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/10 "2024-09-17T18:21:21Z")

</div>

You're right. I was thinking product, and used zip.

Ughhh.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 17, 2024, 6:24pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/11 "2024-09-17T18:24:25Z")

</div>

The double for loop might actually be faster, but it's about conveying intent.  
I read `product`, I know what's coming out.  
It's particularly true when you have more than 2 parameters. A double for loop is simple enough that it might not warrant importing `itertools`, but write the loop equivalent to this:

```python
things = product("abcdef", (1, 2, 3, 4, 5), ['foo', 'bar', 'pox', 'wuz'], {e, w, q, a, e})

```

Things will get ugly real quick.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [September 17, 2024, 6:29pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/12 "2024-09-17T18:29:23Z")

</div>

> [@amarks](#):
>
> The data comprehension in the second one was because they would be in the wrong order if stepped through one by one

read blocking is guaranteed to return the values in the same order as the paths list that is passed in. And because Lists are ordered that order will not change so long as the list is not intentionally modified.

Another tip is if you do need a range function, then you should get in the habit of using `xrange()` as it uses a generator as opposed to creating a full list, which is also good for performance.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 17, 2024, 6:30pm UTC](https://forum.inductiveautomation.com/t/read-udt-instance-vs-read-members/96074/13 "2024-09-17T18:30:36Z")

</div>

Well when I printed the list I generated it showed ~~`Number, Number, Number, Type, Type, Type, Name, Name, Name`~~

~~So, yes I could have adjusted the list prior to the read to get the order I wanted, but I didn't. It's moot now anyway~~ (edit: I was mistaken, it was in the correct order)

We digress...
