# Automation Professionals' Integration Toolkit Module

**URL:** <https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934>\
**Category:** 3rd Party Modules\
**Tags:** perspective, ignition81, ignition83, vision, scripting\
**Created:** [June 13, 2023, 12:42am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934 "2023-06-13T00:42:35Z")\
**Posts on this page:** 20\
**Page:** 9

<div class="post-metadata">

**Author:** ![msteele](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/msteele/32/77889_2.png) [@msteele](https://forum.inductiveautomation.com/u/msteele)\
**Post date:** [August 16, 2024, 3:30pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/161 "2024-08-16T15:30:26Z")

</div>

> [@pturmel](#):
>
> my _copious_ free time.

dibs

---

<div class="post-metadata">

**Author:** ![gnguyen](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@gnguyen](https://forum.inductiveautomation.com/u/gnguyen)\
**Post date:** [August 28, 2024, 11:42am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/162 "2024-08-28T11:42:15Z")

</div>

@Samuel_Sueur, you definitely may trust @pturmel... and best of all, he is speaking french 😉  
see you at icc @pturmel

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 28, 2024, 12:06pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/163 "2024-08-28T12:06:54Z")

</div>

> [@gnguyen](#):
>
> see you at icc

Bien sûr! 😁

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 28, 2024, 7:48pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/164 "2024-08-28T19:48:15Z")

</div>

Automation Professionals is pleased to announce a new version of the Integration Toolkit:

For Ignition v8.1+, as usual: [v2.0.19.242411414](https://www.automation-pros.com/ignition/Integration%20Toolkit-2.0.19.242411414-v81.modl)

Fixes a couple bugs, including the NPE found by @lrose, and adds some new scripting functions:

[system.dataset.orderBy()](https://www.automation-pros.com/toolkit/doc/datasets.html#orderby)

This new `orderBy()` function is very similar to the same-named expression function, but doesn't require you to supply expressions that extract cell values. String arguments are interpreted as column names and turned into the necessary lambda for you (entirely with java code), similar to how IA's `system.dataset.sort()` works, but not limited to a single column key.

[system.dataset.descending()](https://www.automation-pros.com/toolkit/doc/datasets.html#descending)

[system.dataset.naturalOrder()](https://www.automation-pros.com/toolkit/doc/datasets.html#naturalorder)

[system.dataset.naturalCasedOrder()](https://www.automation-pros.com/toolkit/doc/datasets.html#naturalcasedorder)

Note that the latter three are function-returning functions, intended to be used with `system.dataset.orderBy()`. These function-returning functions similarly accept strings as column names, and can be nested. The following will order a dataset by ascending natural order on one column, then descending natural order on a second column:

```python
newDS = system.dataset.orderBy(
	sourceDS,
	system.dataset.naturalOrder('col1'),
	system.dataset.descending(system.dataset.naturalOrder('col2')))

```

---

<div class="post-metadata">

**Author:** ![justin.brzozoski](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justin.brzozoski/32/13452_2.png) [@justin.brzozoski](https://forum.inductiveautomation.com/u/justin.brzozoski)\
**Post date:** [August 28, 2024, 8:08pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/165 "2024-08-28T20:08:25Z")

</div>

I feel as though my post about sorting using arbitrary key functions may have inspired `system.dataset.orderBy`... 🙂

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 28, 2024, 8:11pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/166 "2024-08-28T20:11:57Z")

</div>

Not really. I've had the expression function which sorts by arbitrary key expressions since last year. But I needed a scripted version that can efficiently handle some potentially very large datasets. So an actual jython lambda is suboptimal. (Though you can pass those to my new function if you must.)

---

<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:** [August 29, 2024, 3:42pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/167 "2024-08-29T15:42:39Z")

</div>

Phil,

Perhaps I am missing something, but I don't see any where in the documentation where you show what values can be used to specify each type when providing columninfo to `unionAll()`. Specifically, I am trying to provide a column with type Date.

I have tried "Date","date", and "java.util.Date". Non-of which work. I get an Unsupported inner columnInfo element error.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 29, 2024, 3:49pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/168 "2024-08-29T15:49:48Z")

</div>

I use the same class as IA uses in their CSV tools to handle those strings. Documented here: [system.dataset.fromCSV()](https://www.docs.inductiveautomation.com/docs/8.1/appendix/scripting-functions/system-dataset/system-dataset-fromCSV).

So, "date" should work.

Show your expression?

This test expression:

```python
unionAll(
	asMap('someDate', 'date'),
	asList()
)

```

Produces this empty dataset:

```python
"#NAMES"
"someDate"
"#TYPES"
"date"
"#ROWS","0"

```

---

<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:** [August 29, 2024, 4:09pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/169 "2024-08-29T16:09:57Z")

</div>

Here is the Expression:

```python
unionAll(
	asList(
		"BestByDate","date",
		"CaseCode", "str",
		"FGItem", "str",
		"ShopOrder","I",
		"RecordID", "str",
		"ShopOrderQty","F",
		"CaseUpcCode","str",
		"SerialKey","I",
		"TemplateName","str",
		"GtinCode","str",
		"CaseProductDesc","str",
		"LogoCode","str",
		"CaseCountUnitSize","str",
		"BestBuyFormatCode","str",
		"BB_YEAR4","I",
		"BB_YEAR2","I",
		"BB_MONTHNAME","str",
		"BB_MONTHNUMBER","I",
		"BB_DAY","I",
		"MISC1","str",
		"MISC2","str",
		"MISC3","str",
		"StockNumber","str",
		"DATE_FMT","str"
	),
	forEach(
		objectScript("system.util.sendRequest('<project name>','retrievePalletInfo',{'line':'1A'},'<remoteServer>')"),
		it()[1]
	)
)

```

The `objectScript()` returns the following map:

```python
{u'BB_MONTHNAME': u'December', u'ShopOrderQty': 19208.0, u'DATE_FMT': u' ', u'BestBuyFormatCode': u'XX', u'CaseCode': u'SFY59P1E424208 ', u'FGItem': u'HOUY59PNEL ', u'CaseProductDesc': u'Tomato Ketchup ', u'BB_YEAR4': u'2025', u'BB_DAY': 28, u'BB_MONTHNUMBER': 12, u'MISC3': u' ', u'MISC2': u' ', u'TemplateName': u'M00C2', u'MISC1': u' ', u'BestByDate': 2025-12-28 00:00:00.0, u'BB_YEAR2': 25, u'GtinCode': u'10734730058341', u'CaseUpcCode': u'34730-05834*/* ', u'SerialKey': 395740, u'ShopOrder': 1146113.0, u'LogoCode': u'HOU ', u'StockNumber': u' ', u'RecordID': u'PH', u'CaseCountUnitSize': u'6/114 OZ '}

```

> **Error**
>
> java.lang.Exception: Error executing expression binding on  
> AutoCasePrint.Root Container.Text Area.palletInfo  
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:92)  
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.startup(ExpressionPropertyAdapter.java:113)  
> at com.inductiveautomation.factorypmi.application.binding.DefaultInteractionController.setPropertyAdapter(DefaultInteractionController.java:248)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.bind(ExpressionConfigurator.java:246)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.tryCommit(ExpressionConfigurator.java:192)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor$EditorParent.tryCommit(ConfiguratorMultiplexor.java:398)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor.tryCommit(ConfiguratorMultiplexor.java:546)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.doOK(DynamicOptsDialog.java:95)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog$1.actionPerformed(DynamicOptsDialog.java:64)  
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)  
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)  
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)  
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)  
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)  
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)  
> at java.desktop/java.awt.Component.processEvent(Unknown Source)  
> at java.desktop/java.awt.Container.processEvent(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)  
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)  
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)  
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)  
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)  
> at java.desktop/java.awt.WaitDispatchSupport$2.run(Unknown Source)  
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)  
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)  
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)  
> at java.desktop/java.awt.WaitDispatchSupport.enter(Unknown Source)  
> at java.desktop/java.awt.Dialog.show(Unknown Source)  
> at java.desktop/java.awt.Component.show(Unknown Source)  
> at java.desktop/java.awt.Component.setVisible(Unknown Source)  
> at java.desktop/java.awt.Window.setVisible(Unknown Source)  
> at java.desktop/java.awt.Dialog.setVisible(Unknown Source)  
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.showDialog(DynamicOptsDialog.java:163)  
> at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.openBindingDialog(VisionDesignerImpl.java:1236)  
> at com.inductiveautomation.factorypmi.designer.property.editors.bb.DynamicOptionsButton.actionPerformed(DynamicOptionsButton.java:37)  
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)  
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)  
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)  
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)  
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)  
> at com.jidesoft.plaf.basic.BasicJideButtonListener.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)  
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)  
> at java.desktop/java.awt.Component.processEvent(Unknown Source)  
> at java.desktop/java.awt.Container.processEvent(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)  
> at java.desktop/javax.swing.plaf.basic.BasicTableUI$Handler.repostEvent(Unknown Source)  
> at java.desktop/javax.swing.plaf.basic.BasicTableUI$Handler.mouseReleased(Unknown Source)  
> at com.jidesoft.swing.DelegateMouseInputListener.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)  
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)  
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)  
> at java.desktop/java.awt.Component.processEvent(Unknown Source)  
> at java.desktop/java.awt.Container.processEvent(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)  
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)  
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)  
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)  
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)  
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)  
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)  
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)  
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)  
> at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)  
> Caused by: com.inductiveautomation.ignition.common.expressions.ExpressionException: Unsupported inner columnInfo element BestByDate  
> at com.automation\_pros.simaids.expressions.UnionAll.builderFromColumnInfo(UnionAll.java:71)  
> at com.automation\_pros.simaids.expressions.UnionAll.execute(UnionAll.java:112)  
> at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)  
> at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)  
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:83)  
> ... 102 more
> 
> Ignition v8.1.35 (b2023120517)  
> Java: Azul Systems, Inc. 17.0.8

For context, I am using this in Vision. The reason I am turning a perfectly acceptable map back into a Dataset here is that I would like to store the result of this expression in a custom property. This way it can be accessed multiple times without the need to query across the GAN every time (The function is located on a remote server for other reasons). However, I do not know of any way to store a map in a custom property in Vision without also having all column values converted to strings.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 29, 2024, 4:19pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/170 "2024-08-29T16:19:19Z")

</div>

Replace your first `asList()` with an `asMap()`. When using lists to supply column info, each pair must be a nested list.

FWIW, using `objectScript()` completely breaks the justification for my expression functions. Consider just processing the return value locally in your script.

---

<div class="post-metadata">

**Author:** ![Stian\_Lid\_Kleppe](https://avatars.discourse-cdn.com/v4/letter/s/a5b964/32.png) [@Stian\_Lid\_Kleppe](https://forum.inductiveautomation.com/u/Stian_Lid_Kleppe)\
**Post date:** [September 30, 2024, 7:25pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/171 "2024-09-30T19:25:23Z")

</div>

Is there any reason why

| | | vvm = system.perspective.viewVarMap() |
| --- | --- | --- |
| | | vvm.tagQueryResult = retv |

returns "AttributeError: 'com.automation\_pros.simaids.persist.BindableVarMap' object has no attribute 'tagQueryResult' " for Ignition 8.1.43, but works fine in 8.1.42 ?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [September 30, 2024, 7:57pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/172 "2024-09-30T19:57:46Z")

</div>

Are you sure you are using the latest version of my module in both versions of Ignition? And rebooted, actually. That bug was in a persistent jar that can only be replaced by restarting the service.

---

<div class="post-metadata">

**Author:** ![Stian\_Lid\_Kleppe](https://avatars.discourse-cdn.com/v4/letter/s/a5b964/32.png) [@Stian\_Lid\_Kleppe](https://forum.inductiveautomation.com/u/Stian_Lid_Kleppe)\
**Post date:** [September 30, 2024, 8:14pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/173 "2024-09-30T20:14:49Z")

</div>

I tried to restart the ignition service, but same result. Same version of the modules, different versions of igntion 8.1.42 (running in docker) \<--Working and 8.1.43 (running on windows) \<---Not working

---

<div class="post-metadata">

**Author:** ![Stian\_Lid\_Kleppe](https://avatars.discourse-cdn.com/v4/letter/s/a5b964/32.png) [@Stian\_Lid\_Kleppe](https://forum.inductiveautomation.com/u/Stian_Lid_Kleppe)\
**Post date:** [September 30, 2024, 8:26pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/174 "2024-09-30T20:26:20Z")

</div>

Solved, My bad, restarted the module, and now its working

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 9, 2024, 12:02am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/175 "2024-10-09T00:02:44Z")

</div>

Hey Phil, apologies if you've already answered this, but can I do the equivalent of `','.join(map(str, [10,20,30]))` with your toolkit expression functions? And just in case, what about `"','".join(map(str, [10,20,30]))`?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 9, 2024, 12:09am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/176 "2024-10-09T00:09:00Z")

</div>

As of v8.1.8:

```python
groupConcat(
	forEach(
		asList(10, 20, 30),
		stringFormat("%s", it())
	),
	","
)

```

Adjust to suit.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 9, 2024, 6:40pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/177 "2024-10-09T18:40:48Z")

</div>

For posterity, I'm pretty sure bare `groupConcat` should just work:

```python
groupConcat(
	asList(10, 20, 30),
	","
)

```

Assuming `asList` spits out a regular `java.util.List`.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 9, 2024, 6:42pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/178 "2024-10-09T18:42:41Z")

</div>

`asList()` produces an `ArrayList`, so yes. `groupConcat()` stringifies the elements of the list? (I did not test.)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 9, 2024, 6:44pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/179 "2024-10-09T18:44:13Z")

</div>

Yes, using `Objects.toString`, using empty string for null inputs.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [October 10, 2024, 4:59pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/180 "2024-10-10T16:59:45Z")

</div>

Automation Professionals is pleased to announce a ~~BETA~~ release of its Integration Toolkit, with many new functions.

For Ignition v8.1+: ~~v2.0.20.242841644~~

New/Updated Expression functions:

- [unQualify()](https://www.automation-pros.com/toolkit/doc/scripting.html#unqualify)
- [orderedCopy()](https://www.automation-pros.com/toolkit/doc/scripting.html#orderedcopy)
- [lag()](https://www.automation-pros.com/toolkit/doc/iteration.html#lag)
- [naturalOrder()](https://www.automation-pros.com/toolkit/doc/iteration.html#naturalorder)
- [naturalCasedOrder()](https://www.automation-pros.com/toolkit/doc/iteration.html#naturalcasedorder)
- [jsonDecode()](https://www.automation-pros.com/toolkit/doc/utility.html#jsondecode)
- [jsonEncode()](https://www.automation-pros.com/toolkit/doc/utility.html#jsonencode)

New/Updated Scripting functions:

- [system.util.unQualify()](https://www.automation-pros.com/toolkit/doc/scripting.html#unqualify)
- [system.util.orderedCopy()](https://www.automation-pros.com/toolkit/doc/scripting.html#orderedcopy)
- [system.util.alNumCompare()](https://www.automation-pros.com/toolkit/doc/scripting.html#alnumcompare)

Only **lightly** tested.

Edit: Survived some weeks with a client under heavy use. I consider this production grade, now.

[Previous page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=8)

[Next page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=10)
