diff --git a/.cursor/agents/docs-reviewer.md b/.cursor/agents/docs-reviewer.md new file mode 100644 index 000000000..30f769e76 --- /dev/null +++ b/.cursor/agents/docs-reviewer.md @@ -0,0 +1,18 @@ +--- +name: docs-reviewer +description: Reviewer for CORTEX documentation PRs — conventions, scope, security, and Hugo build risks. +--- + +# Docs reviewer + +Review documentation changes critically but constructively. + +Check: + +1. **Scope** — only intended version(s) and files touched. +2. **Conventions** — frontmatter, `ctx`, shortcodes, shared sections. +3. **Links/media** — `url`/`section`/`figure` paths likely valid; flag new `Cortex.*` keys. +4. **Security** — no secrets, config, or ignored paths in the diff. +5. **Consistency** — tone and structure match sibling pages. + +Provide prioritized findings: must-fix vs nice-to-have. diff --git a/.cursor/agents/docs-writer.md b/.cursor/agents/docs-writer.md new file mode 100644 index 000000000..28eedac1e --- /dev/null +++ b/.cursor/agents/docs-writer.md @@ -0,0 +1,14 @@ +--- +name: docs-writer +description: Technical writer for CORTEX Product Portal Hugo/Docsy content. Use for drafting or restructuring customer-facing documentation. +--- + +# Docs writer + +You write and edit CORTEX product documentation in this Hugo/Docsy repo. + +- Follow [AGENTS.md](../../AGENTS.md) and [content/AGENTS.md](../../content/AGENTS.md). +- Plan multi-page work before editing; prefer `_shared` over duplication. +- Use project shortcodes; never hardcode branding where `{{% ctx %}}` is standard. +- Respect `.cursorignore`; ask for `urls.toml` snippets when adding new `url` paths. +- Keep diffs minimal and limited to requested versions. diff --git a/.cursor/commands/new-doc-page.md b/.cursor/commands/new-doc-page.md new file mode 100644 index 000000000..072c1899d --- /dev/null +++ b/.cursor/commands/new-doc-page.md @@ -0,0 +1,12 @@ +--- +name: new-doc-page +description: Create a new CORTEX documentation page from a sibling template in the requested version and section. +--- + +Create a new documentation page for CORTEX Product Portal. + +1. Confirm doc version (e.g. 2026.3) and target folder under `content/en/docs/{version}/`. +2. Open the closest sibling `_index.md` or leaf `.md` and mirror structure. +3. Apply the add-documentation-page skill workflow. +4. List files created/changed and any `urls.toml` keys the user must add (do not read ignored data files). +5. Summarize nav impact (`weight`, parent section). diff --git a/.cursor/commands/review-docs.md b/.cursor/commands/review-docs.md new file mode 100644 index 000000000..90e1ec320 --- /dev/null +++ b/.cursor/commands/review-docs.md @@ -0,0 +1,14 @@ +--- +name: review-docs +description: Review documentation changes for Hugo conventions, branding, links, and scope. +--- + +Review current doc changes (branch diff or open files). + +1. Frontmatter complete; CORTEX literal in title/linkTitle; `ctx` in body where expected. +2. Shortcodes valid (`section` paths exist under `_shared`, `figure` paths under `content/static`). +3. No edits to ignored paths or unrelated doc versions. +4. Tone matches surrounding customer-facing procedural docs. +5. List risks: missing `urls.toml` keys, cross-version drift, broken shared paths. + +Output: brief bullet list of issues and suggested fixes. diff --git a/.cursor/rules/cortex-docs-repo.mdc b/.cursor/rules/cortex-docs-repo.mdc new file mode 100644 index 000000000..440692986 --- /dev/null +++ b/.cursor/rules/cortex-docs-repo.mdc @@ -0,0 +1,15 @@ +--- +description: CORTEX Product Portal repo purpose, structure, and agent workflow +alwaysApply: true +--- + +# CORTEX docs repository + +- Source for https://docs.wearecortex.com/ (Hugo + Docsy). +- Versioned docs: `content/en/docs/{version}/` with `{version}/_shared/`. +- Shortcodes: `layouts/shortcodes/` (`ctx`, `version`, `section`, `url`, `figure`, …). +- Images: `content/static/{version}/images/`. + +Read [AGENTS.md](../../AGENTS.md) before large edits. Contributions: [CORTEX Support](https://support.wearecortex.com/) per README. + +Canonical page: `content/en/docs/2026.3/getting-started/on-premise/install-innovation-only/single-server-without-ha/architecture.md` diff --git a/.cursor/rules/hugo-content-markdown.mdc b/.cursor/rules/hugo-content-markdown.mdc new file mode 100644 index 000000000..16a35b86f --- /dev/null +++ b/.cursor/rules/hugo-content-markdown.mdc @@ -0,0 +1,13 @@ +--- +description: Hugo/Docsy markdown and frontmatter for CORTEX documentation +globs: content/**/*.md +alwaysApply: false +--- + +# Hugo content + +- Frontmatter: `title`, `linkTitle`, `description`, `weight`. +- Literal CORTEX in `title`/`linkTitle`; `{{% ctx %}}` in body where siblings do. +- Reuse `_shared` + `{{< section "/path.md" >}}` instead of copy-paste. +- Internal links: `{{< url path="Cortex...." >}}` like neighboring pages. +- Procedural tone; no drive-by edits across doc versions. diff --git a/.cursor/rules/hugo-shortcodes.mdc b/.cursor/rules/hugo-shortcodes.mdc new file mode 100644 index 000000000..58091abd2 --- /dev/null +++ b/.cursor/rules/hugo-shortcodes.mdc @@ -0,0 +1,20 @@ +--- +description: Custom Hugo shortcode usage and constraints +globs: content/**/*.md, layouts/shortcodes/** +alwaysApply: false +--- + +# Shortcodes + +Read `layouts/shortcodes/*.html` before changing usage. + +| Shortcode | Notes | +| --- | --- | +| `ctx` | Renders CORTEX; not in `title`/`linkTitle` | +| `version` | Uses `{version}/_shared/currentVersion.md` | +| `section` | Path under `{version}/_shared/`; must exist | +| `url` | Keys from `urls.toml` (ignored — ask user for keys) | +| `figure` | `src` under `/images/...`; file in `content/static/{version}/images/` | +| `image` | `src` under `/images/...`; file in `content/static/{version}/images/` | + +Template edits affect all versions — scope testing accordingly. diff --git a/.cursor/rules/security-cursorignore.mdc b/.cursor/rules/security-cursorignore.mdc new file mode 100644 index 000000000..20bbdd05a --- /dev/null +++ b/.cursor/rules/security-cursorignore.mdc @@ -0,0 +1,12 @@ +--- +description: Respect .cursorignore — no secrets, config, or data file access +alwaysApply: true +--- + +# Security + +Follow [.cursorignore](../../.cursorignore). + +Do not read/commit: `config.*`, `data/**`, `urls.toml`, CI configs, `.env*`, certs/keys, `public/**`, `node_modules/**`, internal/draft content paths. + +If link keys are needed, copy from an existing page in the same section or ask the user. diff --git a/.cursor/skills/add-documentation-page/SKILL.md b/.cursor/skills/add-documentation-page/SKILL.md new file mode 100644 index 000000000..100990de4 --- /dev/null +++ b/.cursor/skills/add-documentation-page/SKILL.md @@ -0,0 +1,28 @@ +--- +name: add-documentation-page +description: Add a new Hugo documentation page under a versioned CORTEX docs section. Use when creating topics, install steps, reference pages, or _index.md section roots. +--- + +# Add documentation page + +## When to use + +- New leaf page or section `_index.md` under `content/en/docs/{version}/` +- User names a doc version (e.g. 2026.3) and section path + +## Steps + +1. Find a sibling page in the same folder; copy frontmatter shape (`title`, `linkTitle`, `description`, `weight`). +2. Set `weight` relative to neighbors (lower = earlier in sidebar). +3. Use literal CORTEX in `title`/`linkTitle`; `{{% ctx %}}` in body. +4. For install/guide style, use `# {{% param title %}}` and match callouts (`pageinfo`, `alert`). +5. Add next-step links with `{{< url path="Cortex...." >}}` — reuse keys from sibling pages. +6. If content repeats across pages, add `{version}/_shared/...` and `{{< section "/path.md" >}}` instead. +7. Do not edit other version trees unless requested. + +## Checklist + +- [ ] Frontmatter complete +- [ ] Branding matches `ctx` convention +- [ ] Links follow section `url` patterns +- [ ] Scope limited to requested version diff --git a/.cursor/skills/edit-versioned-images/SKILL.md b/.cursor/skills/edit-versioned-images/SKILL.md new file mode 100644 index 000000000..39b6ca8b0 --- /dev/null +++ b/.cursor/skills/edit-versioned-images/SKILL.md @@ -0,0 +1,24 @@ +--- +name: edit-versioned-images +description: Add or update versioned diagrams and screenshots under content/static. Use for figures, architecture diagrams, or draw.io editable assets. +paths: content/static/**/* +--- + +# Versioned images + +## When to use + +- New/updated `{{< figure >}}` or diagram in docs +- User mentions draw.io, editable folder, or architecture screenshots + +## Steps + +1. Confirm doc version → static folder `content/static/{version}/images/`. +2. Editable diagrams: `images/editable/` (see `content/static/2026.3/images/_ReadMe.md`). +3. Reference in markdown: `{{< figure src="/images/editable/....png" class="centre" title="..." >}}`. +4. Ensure file exists on disk before referencing (figure shortcode checks `content/static/{version}/...`). +5. For new versions, mirror structure from the latest version's `images/` tree when appropriate. + +## Screenshot conventions (from _ReadMe) + +- Service Fabric screenshots: 1500×700, device emulation, refresh off where noted. diff --git a/.cursor/skills/shared-sections/SKILL.md b/.cursor/skills/shared-sections/SKILL.md new file mode 100644 index 000000000..acafa71bb --- /dev/null +++ b/.cursor/skills/shared-sections/SKILL.md @@ -0,0 +1,25 @@ +--- +name: shared-sections +description: Create or edit reusable _shared markdown included via the section shortcode. Use for duplicated install, architecture, or upgrade steps within a doc version. +paths: content/**/_shared/**/*.md +--- + +# Shared sections + +## When to use + +- Same paragraph/block appears on multiple pages in one version +- User asks to DRY up install/architecture/upgrade content + +## Steps + +1. Locate `{version}/_shared/` (e.g. `content/en/docs/2026.3/_shared/`). +2. Find existing fragment before creating a new file. +3. Add or edit `.md` under `_shared/` with normal markdown (no page frontmatter required for fragments). +4. Include from pages: `{{< section "/relative/path/from/_shared.md" >}}` (leading path as in `architecture.md`). +5. Fix all includers if you rename or move a shared file. + +## Pitfalls + +- Path must resolve for the page's version or Hugo errors at build. +- Shared files are per-version; syncing across 2026.3 / 2025.9 requires explicit user approval. diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 000000000..a580df2d1 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,103 @@ +# ============================================ +# Hugo / Docsy Sensitive Data Protection +# ============================================ + +# Core secrets (keep from previous list) +.env +.env.* +*.env +*.secret +*.secrets + +# Hugo config files (frequently contain URLs, tokens, etc.) +config.toml +config.yaml +config.yml +config.json + +# If you split config: +config/**/production* +config/**/prod* +config/**/secrets* +config/**/private* + +# Hugo module cache (can include fetched private modules) +resources/_gen/** +.hugo_build.lock + +# Content drafts / unpublished content +content/**/drafts/** +content/**/_drafts/** +content/**/private/** +content/**/internal/** + +# Frontmatter-heavy markdown (may contain PII / metadata leaks) +# (Optional depending on your workflow) +# content/**/*.md + +# Data files (VERY important in Hugo) +data/** +*.yaml +*.yml +*.json +*.toml +*.csv + +# Docsy-specific +# Includes navigation and structure that may leak internal structure +content/en/docs/internal/** +content/en/docs/private/** +content/en/docs/drafts/** + +# Static assets (can accidentally contain secrets) +static/uploads/** +static/private/** +static/internal/** +static/files/** +static/data/** + +# Built output (can contain rendered secrets if misconfigured) +public/** + +# Scripts / build tooling +scripts/** +tools/** + +# Node dependencies (Docsy commonly uses npm tooling) +node_modules/** +package-lock.json +pnpm-lock.yaml +yarn.lock + +# CI/CD configs (often contain refs to secrets) +.github/workflows/** +.gitlab-ci.yml +.azure-pipelines.yml + +# Logs / temporary files +*.log +tmp/** +.cache/** + +# ============================================ +# Global sensitive patterns +# ============================================ +**/*password* +**/*secret* +**/*credential* +**/*token* +**/*apikey* +**/*private* + +# Certificates & keys +*.pem +*.key +*.crt +*.p12 +*.pfx + +# Cloud credentials +.aws/** +.azure/** +.gcp/** +*service-account*.json \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..554300f0b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,59 @@ +# CORTEX Product Portal — Agent Instructions + +This repository is the source for the [CORTEX Product Portal](https://docs.wearecortex.com/) (Hugo + Docsy). Use these instructions when editing documentation, layouts, or static assets. + +## Repository purpose + +- **Site**: Public product documentation for the CORTEX automation platform. +- **Stack**: Hugo static site with Docsy theme, custom shortcodes under `layouts/shortcodes/`, versioned content under `content/en/docs/`. +- **Contributions**: Per [README.md](README.md), external contributions and feedback go through [CORTEX Support](https://support.wearecortex.com/). Do not assume undocumented publish or deploy steps. + +## How to work in this repo + +### Plan before large edits + +For multi-page or cross-version changes, outline affected paths first (which version folders, `_shared` snippets, `urls.toml` entries, images). Prefer small, reviewable diffs over sweeping rewrites. + +### Versioned documentation + +- Product docs live under `content/en/docs/{version}/` (e.g. `2026.3`, `2025.9`, `2024.9`). +- Each version has a `_shared/` folder for reusable sections included via the `section` shortcode. +- When changing behavior that applies to multiple releases, identify whether content is duplicated per version or centralized in `_shared` before editing. +- Canonical example: `content/en/docs/2026.3/getting-started/on-premise/install-innovation-only/single-server-without-ha/architecture.md` + +### Branding and naming + +- In page **body** text, use `{{% ctx %}}` for the product name — do not hardcode "CORTEX" in prose when the shortcode applies. +- In YAML **`title`** and **`linkTitle`**, use the literal name (e.g. `CORTEX`) — shortcodes are not used there. +- Release label in prose: `{{< version >}}` (reads `{version}/_shared/currentVersion.md`). + +### Images and diagrams + +- Versioned assets: `content/static/{version}/images/` (`editable/` for draw.io). +- Use `{{< figure src="/images/..." >}}`; see `layouts/shortcodes/figure.html`. +- Diagram notes: `content/static/2026.3/images/_ReadMe.md` + +### Security and ignored paths + +Respect [.cursorignore](.cursorignore). Do not read or commit Hugo config, `data/**`, CI workflows, secrets, or internal/draft paths. If `urls.toml` is needed, ask the user for the relevant key or snippet. + +## Project agent assets + +| Asset | Location | +| --- | --- | +| Rules | `.cursor/rules/*.mdc` | +| Skills | `.cursor/skills/*/SKILL.md` | +| Commands | `.cursor/commands/*.md` | +| Agents | `.cursor/agents/*.md` | + +## Quality bar + +- Procedural, customer-facing tone; preserve frontmatter (`title`, `linkTitle`, `description`, `weight`). +- Prefer shortcodes over raw HTML; match `{{< url path="Cortex...." >}}` patterns in the same section. +- Scope changes to the version(s) the user requested. + +## What not to do + +- Do not modify `public/`, `node_modules/`, or ignored config/data files. +- Do not invent install commands or CI steps not documented in-repo. +- Do not remove or weaken `.cursorignore` without explicit user approval. diff --git a/content/AGENTS.md b/content/AGENTS.md new file mode 100644 index 000000000..4d844c0e6 --- /dev/null +++ b/content/AGENTS.md @@ -0,0 +1,31 @@ +# Content authoring (Hugo / Docsy) + +Applies when editing files under `content/`. + +## Page types + +| File | Purpose | +| --- | --- | +| `_index.md` | Section landing; `linkTitle` + `weight` control nav | +| `*.md` | Leaf topic | +| `_shared/**/*.md` | Fragments via `{{< section "/path.md" >}}` | + +## Frontmatter + +```yaml +--- +title: "Page Title" +linkTitle: "Nav Label" +description: "Summary for indexes and SEO." +weight: 10 +--- +``` + +- `title` / `linkTitle`: literal **CORTEX**, not `{{% ctx %}}`. +- `description`: may use `{{% ctx %}}` in prose. + +## Body + +- Match siblings: `# {{% param title %}}`, `pageinfo`, `alert`, `figure`, `url`, `section`, `tab`/`tabpane`. +- Search `{version}/_shared/` before duplicating paragraphs. +- Do not edit other version folders unless explicitly asked. diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md index f8f540589..7eea2d1be 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md @@ -1,50 +1,162 @@ --- title: "Indexes" linkTitle: "Indexes" -description: "Information related to working with Indexes." +description: "Information related to working with zero-based indexes in ordered collections and strings." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +An **index** is a zero-based position that identifies which [item][] to read or update in an ordered collection, or which character to access in a [String][]. + +Indexes answer the question “which position?”—not “which matching value?”. When several items share the same value, use an [occurrence][] (and blocks such as [Get Index Of Item With Value][]) to select the nth match, then use the returned index if you need a position. See [Indexes vs occurrences](#indexes-vs-occurrences). + +| Data type | Uses indexes? | Notes | +| --- | --- | --- | +| [List<TItem>][] | Yes | Primary ordered collection in flows | +| [Array][] | Yes | Fixed size; same index rules as lists | +| [String][] | Yes | Index refers to a character position | +| [Dictionary<TKey, TItem>][] | No | Items are accessed by [key][], not index | +| [Structure][] | No | Items are accessed by [key][] or property name | +| [QueueWithPriority<TItem, TPriority>][] | No | Items are not randomly accessed by index | +| [Data Storage Collection][] | No | Items are accessed by [key][] only | + +For how collection types differ, see [What is a Collection?][]. + +## Zero-based numbering + +{{% ctx %}} indexes follow standard C# conventions ([zero based][]): + +* The first item (or character) is at index `0`. +* The second is at index `1`, and so on. +* For a list with `n` items, valid indexes are `0` through `n - 1` inclusive. + +For example, in `["A", "B", "C"]` the item `"B"` is at index `1`, and the last item is at index `2` (not `3`). + +Block properties named `Index` are typically [Int32][] values within this range. An index outside the range may cause [PropertyValueOutOfRangeException][] (for example on [Get Item At Index][] when the list is empty or the index is too large). + +## Accessing items by index + +### In the Expression Editor + +Use [index expressions][] to read or assign items in [List][] and [Array][] variables, and to work with [String][] characters. Examples: + +| Expression | Result (example list `[1, 2, 3, 4, 5]`) | +| --- | --- | +| `($)List[0]` | `1` (first item) | +| `($)List[2]` | `3` (third item) | +| `($)List[^1]` | `5` (last item) | +| `($)List[0..2]` | `[1, 2]` (first two items) | +| `($)List[2..]` | `[3, 4, 5]` (from third item to end) | + +[Dictionary][] and [Structure][] items use the same bracket syntax, but the value inside the brackets is a [key][], not a numeric index—for example `($)Dictionary["FirstKey"]`. + +Range and index-from-end syntax (`^1`, `..`) follow C# rules. For more examples and behaviour, see [Index expressions][] in the [Expression Editor][] documentation and [Indices and Ranges][] in the C# language reference. + +### With blocks + +Many [List][] and text blocks expose an `Index` property ([Int32][]) to select a position directly: + +* **Lists** — [Get Item At Index][], [Set Item At Index][], [Add Item At Index][], [Remove Item At Index][], and related blocks that operate on one or more indexes +* **Text** — [Get Text At Index][], [Add Text Before Index][], [Remove Text At Index][], and related blocks (indexes refer to character positions in the string) + +Some blocks return an index as output—for example [Get Index Of Item With Value][] writes the zero-based position of the specified [occurrence][] of a matching item, or `-1` if no match is found. + +## Indexes vs occurrences + +Indexes and [occurrences][] solve different problems: -- Overview/summary -- What is an Index? -- How are they accessed? -- Indexes are zero based -- Difference between occurrence and indexes +| | **Index** | **Occurrence** | +| --- | --- | --- | +| Meaning | Position in the collection (0-based) | Which matching item when searching by value (1-based from start, or negative from end) | +| Used when | You already know the position | Duplicate values exist and you need the 1st, 2nd, or last match | +| Typical use | `list[2]`, [Get Item At Index][] | [Get Index Of Item With Value][] with `Occurrence` = `1` or `-1` | +| Example list `[1, 2, 1]` | Index `0` → `1`, index `2` → `1` | 1st occurrence of value `1` → index `0`; 2nd occurrence → index `2` | -## Accessing an item using Indexes +After [Get Index Of Item With Value][] returns an index, you can pass that value to blocks or expressions that expect a position. -[Indexes][] can be used in the [Expression Editor][] to access items in a Collection. +For occurrence property values and behaviour when a match is missing, see [Occurrences][]. ## Remarks ### Known Limitations -TODO +None ## See Also ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][] +* [Keys][] +* [Occurrences][] +* [Working with Text][] ### Related Data Types -TODO +* [List<TItem>][] +* [Array][] +* [IList<TItem>][] +* [String][] +* [Int32][] ### Related Blocks -TODO +* [List][] blocks (for example [Get Item At Index][], [Set Item At Index][], [Add Item At Index][], [Remove Item At Index][]) +* Text blocks (for example [Get Text At Index][], [Get Text Between Indexes][], [Get Index Of Text][]) ### External Documentation -TODO +* [Indices and ranges (C#)][MS Indices and Ranges] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [System.String][MS String] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[item]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Working with Text]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.MainDoc" >}} +[zero based]: {{< url path="Cortex.Reference.Glossary.U-Z.ZeroBased" >}} + +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} -[Indexes]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} + +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Set Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemAtIndex.MainDoc" >}} +[Add Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtIndex.MainDoc" >}} +[Remove Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemAtIndex.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} + +[Get Text At Index]: {{< url path="Cortex.Reference.Blocks.Text.GetText.GetTextAtIndex.MainDoc" >}} +[Get Text Between Indexes]: {{< url path="Cortex.Reference.Blocks.Text.GetText.GetTextBetweenIndexes.MainDoc" >}} +[Add Text Before Index]: {{< url path="Cortex.Reference.Blocks.Text.AddText.AddTextBeforeIndex.MainDoc" >}} +[Remove Text At Index]: {{< url path="Cortex.Reference.Blocks.Text.RemoveText.RemoveTextAtIndex.MainDoc" >}} +[Get Index Of Text]: {{< url path="Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText.MainDoc" >}} + +[PropertyValueOutOfRangeException]: {{< url path="Cortex.Reference.Exceptions.Common.Property.PropertyValueOutOfRangeException.MainDoc" >}} + +[MS Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS String]: {{< url path="MSDocs.DotNet.Api.System.String.MainDoc" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md index 816bde126..4f2d567ad 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md @@ -1,25 +1,85 @@ --- title: "Items" linkTitle: "Items" -description: "Information related to working with Items." +description: "Information related to working with items in collections." weight: 100 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +An **item** is a single value stored in a [collection][]. Collections group items so they can be passed between blocks, read or updated in expressions, and processed in a flow. + +In collection data types, the item type is usually written as `TItem` (a [generic type parameter][]). For example, in `List` each item is an `Int32`; in `Dictionary` each item may be any data type. See [What is a Collection?][] for how collection types differ. + +| Collection type | How items are identified | Item type parameter | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | [Key][] | `TItem` | +| [Structure][] | [String][] [key][] or property name | Any data type | +| [List<TItem>][] | Zero-based [index][] | `TItem` | +| [Array][] | Zero-based [index][] | Element type of the array | +| [QueueWithPriority<TItem, TPriority>][] | Position in queue (not random access) | `TItem` | +| [Data Storage Collection][] | [String][] [key][] (`Data` value) | [dynamic][] | + +Items are not the same as [keys][]: in key/item collections, the key is used to look up the item. In ordered collections, the [index][] (or [occurrence][] when matching by value) identifies which item you mean. + +## Item data types + +### Homogenous and heterogenous items + +A collection is [homogenous][] when every item has the same data type (for example `List` or `Dictionary`). It is [heterogenous][] when items may have different data types (for example `List`, `List`, or `Dictionary`). + +Literal syntax in the [Expression Editor][] often infers item types from the values you provide—for example `[1, 2, 3]` creates a `List`, while `["Some Text", true, 1]` creates a `List`. See [Create a List<TItem>][] and [Create a Dictionary<TKey, TItem>][]. + +### Null items + +Whether an item can be `null` depends on the collection and how it is used: + +* [Keys][] cannot be `null` in dictionaries and structures. +* Items may be `null` in some collections (for example a `List` may contain `null` entries). Some blocks throw [InvalidPropertyValueException][] when `null` is not allowed for the declared `TItem` type. + +Check the block or data type documentation for the collection you are using. + +## Accessing items + +### In the Expression Editor + +How you read or assign an item depends on the collection type: + +| Collection type | Typical syntax | More information | +| --- | --- | --- | +| [List<TItem>][] / [Array][] | `list[0]`, `list[^1]`, `list[0..2]` | [Index expressions][], [Indexes][] | +| [Dictionary<TKey, TItem>][] | `dictionary["Key1"]` | [Index expressions][], [Keys][] | +| [Structure][] | `structure.Name` or `structure["any-key"]` | [Property expressions][], [Index expressions][] | + +[List][] and [Array][] items use zero-based indexes. [Dictionary][] and [Structure][] items use keys. Range and index-from-end syntax (`^1`, `..`) follow C# rules; see [Indices and Ranges][] and the [Expression Editor][] documentation. + +### With blocks + +[Dictionary][], [List][], [Queue][], and [Data Storage][] blocks expose properties such as `Item` and `Data` for the item being added, read, updated, or removed. Access patterns include: + +* **By key** — [Get Item With Key][], [Set Item With Key][], [Read Data With Key][] +* **By index** — [Get Item At Index][], [Set Item At Index][] +* **By value and occurrence** — [Remove Item With Value][], [Get Index Of Item With Value][] (see [Occurrences][]) +* **Queue order** — [Enqueue Item][], [Dequeue Item][], [Peek Item][] (items are not selected by index or key) + +For a full list of collection types and access patterns, see [What is a Collection?][]. + +## Comparing and matching items + +Many blocks need to know whether an item in a collection **matches** a value you supply—for example when removing items with a given value or checking whether a key is already present. + +How matching works depends on whether you use C# expressions or blocks: + +| Context | How equality is determined | +| --- | --- | +| C# syntax in the [Expression Editor][] | [Reference equality][] for reference types and [value equality][] for value types, following standard C# rules (including `==` and `Equals` where applicable). | +| [List][] and [Dictionary][] blocks | For reference types, [reference equality][] is tried first; if no matching reference is found, comparison falls back to [value equality][]. For value types, [value equality][] is used. | + +This difference matters when items are reference types (for example two different `Structure` instances with the same field values). Blocks may treat them as equal when C# `==` would not. -- Overview/summary -- What is an Item? -- How are they accessed? - - Link to Indexes - - Link to Keys -- If you are using C# syntax, then the equality is done using reference equality for reference types, and value equality for value types -- If you are using List of Dictionary blocks, then the equality is done using reference equality for reference types and falls back to value equality if no reference was found, and value equality for value types +For examples and affected blocks, see [Object Equality][]. For general C# equality rules, see [Equality comparisons][MS Equality]. ## Remarks @@ -31,16 +91,97 @@ None ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Keys][] +* [Indexes][] +* [Occurrences][] +* [Generics][] +* [Object Equality][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [List<TItem>][] +* [Array][] +* [QueueWithPriority<TItem, TPriority>][] +* [IDictionary<TKey, TItem>][] +* [IList<TItem>][] +* [dynamic][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][], [Get Item With Key][]) +* [List][] blocks (for example [Add Item At End][], [Get Item At Index][], [Remove Item With Value][]) +* [Queue][] blocks (for example [Enqueue Item][], [Dequeue Item][], [Peek Item][]) +* [Data Storage][] blocks (for example [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [Equality comparisons (C#)][MS Equality] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[collection]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[generic type parameter]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[homogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Homogenous" >}} +[heterogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Heterogenous" >}} +[reference equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[value equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} + +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Queue]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} + +[Create a List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.CreateNew" >}} +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} +[Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} + +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd.MainDoc" >}} +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Set Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemAtIndex.MainDoc" >}} +[Remove Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemWithValue.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} +[Enqueue Item]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue Item]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek Item]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} + +[InvalidPropertyValueException]: {{< url path="Cortex.Reference.Exceptions.Flows.Blocks.InvalidPropertyValueException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS Equality]: {{< url path="MSDocs.CSharp.EqualityOperators" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md index 7361f5a06..245af4e15 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md @@ -1,37 +1,123 @@ --- title: "Keys" linkTitle: "Keys" -description: "Information related to working with Keys." +description: "Information related to working with keys in key/item collections such as dictionaries and structures." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +A **key** is the value used to look up a specific [item][] in a key/item collection. Keys answer the question “which entry?”—not “which position?” (that is an [index][]) and not “which matching value?” (that is an [occurrence][] when searching by item value). + +| Collection type | Key data type | Notes | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | `TKey` ([generic type parameter][]) | Each dictionary declares its own key type | +| [Structure][] | [String][] only | Behaves like a heterogenous string-keyed dictionary | +| [Data Storage Collection][] | [String][] only | Persisted; keys are case sensitive | +| [List<TItem>][] / [Array][] | N/A | Items are accessed by [index][], not key | +| [QueueWithPriority<TItem, TPriority>][] | N/A | Items are not randomly accessed by key | + +For how collection types differ, see [What is a Collection?][]. + +Keys are not the same as [items][]: the key identifies the entry; the item is the value stored at that entry. In expressions, both [Dictionary][] and [Structure][] use bracket syntax to supply a key—for example `dictionary["Key1"]`—even though the syntax is called an [index expression][] in the [Expression Editor][] documentation. See [Keys vs indexes](#keys-vs-indexes). + +## Key data types + +### Dictionary<TKey, TItem> + +In [Dictionary<TKey, TItem>][], `TKey` is the [generic type parameter][] that defines the data type of every key. `TItem` is the type of each stored item. For example: + +* `Dictionary` — keys are [String][], items are [Int32][] +* `Dictionary` — keys are [Int32][], items are [String][] + +When you create a dictionary in the [Expression Editor][], the key types are inferred from the keys you provide—for example `new Dictionary() { { "A", 1 } }`. See [Create a Dictionary<TKey, TItem>][]. + +{{% ctx %}} supports a defined set of types for `TKey` in practice (including [String][], [Int32][], [Double][], [Boolean][], and [DateTimeOffset][]). Some reference types (for example [IList][]<[Int32][]>) can also be used as keys; behaviour then depends on how that type implements equality. See [Uniqueness](#uniqueness-and-null) and the Known Limitations on [Dictionary<TKey, TItem>][]. + +### Structure + +A [Structure][] always uses [String][] keys. Items may be any data type. JSON object literals in the [Expression Editor][] create a [Structure][], not a [Dictionary][]. + +### Data Storage Collection + +Entries in a [Data Storage Collection][] are identified by a [String][] key and a [dynamic][] `Data` value. Keys are case sensitive (`"user"` and `"USER"` are different keys). Collection names are case insensitive within a scope. See [Write Data With Key][]. + +## Uniqueness and null + +### Keys cannot be null + +A key cannot be `null` when adding, reading, updating, or removing entries in [Dictionary][], [Structure][], or [Data Storage Collection][] variables. Blocks such as [Add Item With Key][] throw [PropertyNullException][] if the key property is `null`. + +### Keys must be unique + +Within a single collection instance, each key must be unique so that lookups are unambiguous. + +For typical scalar keys ([String][], [Int32][], [Boolean][], and similar), two keys with the same value are considered the same key. Adding a second entry with an equal key updates or conflicts with the existing entry (for example [KeyPresentException][] on [Add Item With Key][]). -- Overview/summary -- What is a Key? -- How are they accessed? -- The type of the Key depends on the data type -- Keys are unique -- Keys cannot be null -- If you are using C# syntax, then the equality is done using reference equality for reference types, and value equality for value types -- If you are using Dictionary blocks, then the equality is done using reference equality for reference types and falls back to value equality if no reference was found, and value equality for value types +For some reference-type keys, equality follows C# reference semantics: two keys are the same only if they refer to the same object, not merely if their contents match. That can allow multiple entries whose keys look the same when printed but are different object references—for example two separate `List` instances both containing `[1]`. Dictionary blocks that support [occurrence][] when removing by key document this behaviour; see [Remove Item With Key][]. -## Accessing an item using Keys +## Comparing keys -[Keys][] can be used in the [Expression Editor][] to access items in a Collection. +Blocks and expressions need a consistent rule for whether a key you supply **matches** a key already in the collection—for example when checking [Contains Item With Key][] or adding with [Add Item With Key][]. + +| Context | How key equality is determined | +| --- | --- | +| C# syntax in the [Expression Editor][] | [Reference equality][] for reference types and [value equality][] for value types, following standard C# rules (including `==` and `Equals` where applicable). | +| [Dictionary][] and [Data Storage][] blocks | For reference types, [reference equality][] is tried first; if no matching reference is found, comparison falls back to [value equality][]. For value types, [value equality][] is used. | + +This difference matters when keys are reference types. A block may treat two keys as equal when C# `==` would not, or when duplicate reference-type keys exist in the same dictionary. For examples and affected blocks, see [Object Equality][]. For general C# rules, see [Equality comparisons][MS Equality]. + +## Accessing items using keys + +### In the Expression Editor + +Use an [index expression][] to read or assign an item by key: + +| Collection type | Example | More information | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | `($)Dictionary["Key1"]` | [Index expressions][] | +| [Structure][] | `($)Structure.Name` or `($)Structure["any-key"]` | [Property expressions][] (identifier keys), [Index expressions][] | + +[Structure][] keys that are valid C# identifiers can use property syntax (`structure.Count`). Any string key can use bracket syntax (`structure["any-key"]`). + +### With blocks + +Pass the key to the block’s `Key` property (type `TKey` or [String][] as appropriate): + +* **Dictionary** — [Add Item With Key][], [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Contains Item With Key][], and related blocks +* **Data Storage** — [Read Data With Key][], [Write Data With Key][] (overwrites existing data when the key already exists) + +For a full list of collection access patterns, see [Items][] and [What is a Collection?][]. + +## Keys vs indexes + +[Indexes][] are zero-based positions in **ordered** collections ([List][], [Array][], [String][] characters). **Keys** identify entries in **key/item** collections. + +Both use square brackets in the [Expression Editor][], but the expression inside the brackets means something different: + +| Syntax | Meaning | +| --- | --- | +| `list[0]` | First item (index `0`) | +| `dictionary["Key1"]` | Item whose key is `"Key1"` | + +Using a numeric index on a [Dictionary][] or [Structure][] is not valid. Using a string key on a [List][] is not valid. See [Indexes][]. ## Remarks ### Known Limitations -#### Complex Keys do not show sho correctly in the Variable Details Viewer +#### Supported key types + +Only certain data types can be used for `TKey` in [Dictionary<TKey, TItem>][] and [IDictionary<TKey, TItem>][] properties. These include, but are not limited to, [String][], [Int32][], [Double][], [Boolean][], and [DateTimeOffset][]. See the Known Limitations section on [Dictionary<TKey, TItem>][]. + +#### Non-String keys in Gateway + +If `TKey` is not [String][], Gateway displays the key using its `ToString()` representation (for example an [Int32][] key `1` may appear as `"1"`). + +#### Complex keys do not show correctly in the Variable Details Viewer -Currently, if a Dictionary is shown in the Variable Details Viewer and contains Complex Data types as its keys, the data within the variable will not be displayed correctly. +Currently, if a [Dictionary][] is shown in the Variable Details Viewer and contains complex data types as its keys, the data within the variable will not be displayed correctly. In the future this limitation may be removed. @@ -39,19 +125,84 @@ In the future this limitation may be removed. ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][] +* [Indexes][] +* [Occurrences][] +* [Generics][] +* [Object Equality][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [IDictionary<TKey, TItem>][] +* [String][] +* [dynamic][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][], [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Contains Item With Key][]) +* [Data Storage][] blocks (for example [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.IDictionary<TKey, TItem>][MS IDictionary] +* [Equality comparisons (C#)][MS Equality] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[item]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[generic type parameter]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[reference equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[value equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} + +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IList]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} +[Double]: {{< url path="Cortex.Reference.DataTypes.Numbers.Double.MainDoc" >}} +[Boolean]: {{< url path="Cortex.Reference.DataTypes.ConditionalLogic.Boolean.MainDoc" >}} +[DateTimeOffset]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeOffset.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} + +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} -[Keys]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[index expression]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} + +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Remove Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithKey.MainDoc" >}} +[Contains Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.ContainsItem.ContainsItemWithKey.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} + +[PropertyNullException]: {{< url path="Cortex.Reference.Exceptions.Common.Property.PropertyNullException.MainDoc" >}} +[KeyPresentException]: {{< url path="Cortex.Reference.Exceptions.Dictionaries.KeyPresentException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS IDictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IDictionary" >}} +[MS Equality]: {{< url path="MSDocs.CSharp.EqualityOperators" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md index ceec95a19..c6632756b 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md @@ -1,47 +1,135 @@ --- title: "Occurrences" linkTitle: "Occurrences" -description: "Information related to working with Occurrences." +description: "Information related to selecting the nth matching item or text segment when duplicates exist, using one-based or negative occurrence values." --- # {{% param title %}} ## Summary -TODO +An **occurrence** selects which **match** you mean when the same value, key, or text appears more than once. Occurrences answer the question “which duplicate?”—not “which position?” (that is an [index][indexes]). -- Summary -- What is an Occurrence? -- How are they accessed? - - Positive or Negative int properties on blocks that operate on single items -- Difference between occurrence and indexes -- specified occurrence +In {{% ctx %}}, occurrence values are [Int32][] inputs on block properties named `Occurrence`. They are **one-based when counting from the start** (`1` = first match, `2` = second match) and **negative when counting from the end** (`-1` = last match, `-2` = second from last). The value `0` is not valid for blocks that require a specified occurrence; see [Occurrence not present](#occurrence-not-present). -## Positive Occurrences +| Context | What is counted | Order | +| --- | --- | --- | +| [List<TItem>][] matching by [item][items] value | Items equal to the supplied value ([Object Equality][]) | Collection order (left to right) | +| [Dictionary<TKey, TItem>][] matching by value | Items equal to the supplied value | See [Dictionaries and duplicate entries](#dictionaries-and-duplicate-entries) | +| [Dictionary<TKey, TItem>][] matching by [key][keys] | Entries with the same key (for example duplicate reference-type keys) | See [Dictionaries and duplicate entries](#dictionaries-and-duplicate-entries) | +| [String][] text search | Matches of [Text To Find][] in the string | Left to right in the text | -TODO: +After a block returns an [index][indexes] (for example [Get Index Of Item With Value][] or [Get Index Of Text][]), use that position with index-based blocks or [index expressions][]. See [Occurrences vs indexes](#occurrences-vs-indexes). -- Get First, Second, Third, Nth +## Positive occurrences -## Negative Occurrences +A **positive** occurrence counts from the **first** match: -TODO: +| Occurrence | Meaning | +| --- | --- | +| `1` | First match | +| `2` | Second match | +| `3` | Third match | +| `n` | nth match | -- Get Last, Second from Last, Third from Last, Nth from Last +### Example (list by value) + +For the list `[1, 2, 3, 3, 2, 1]` and value `1`: + +| Occurrence | Item at that occurrence | Zero-based [index][indexes] returned by [Get Index Of Item With Value][] | +| --- | --- | --- | +| `1` | First `1` | `0` | +| `2` | Second `1` | `5` | + +Block examples use the same list: [Get Index Of Item With Value][], [Set Item With Value][], and [Remove Item With Value][]. + +## Negative occurrences + +A **negative** occurrence counts from the **last** match: + +| Occurrence | Meaning | +| --- | --- | +| `-1` | Last match | +| `-2` | Second from last | +| `-3` | Third from last | +| `-n` | nth from last | + +### Example (list by value) + +For `[1, 2, 3, 3, 2, 1]` and value `1`: + +| Occurrence | Item at that occurrence | Zero-based index | +| --- | --- | --- | +| `-1` | Last `1` | `5` | +| `-2` | Second-to-last `1` | `0` | + +If there are `m` matches, valid negative occurrences are `-1` through `-m` inclusive (for example three matches allow `-1`, `-2`, and `-3`). + +## Accessing items using occurrences + +Occurrences are used in the **block editor**, not as a separate expression syntax. Pass an [Int32][] value to the block’s `Occurrence` property (often via the [Literal][] editor; default is commonly `1`). + +Typical patterns: + +* **Find position** — [Get Index Of Item With Value][], [Get Index Of Text][], then use the returned index elsewhere +* **Read** — [Get Item With Key][] when duplicate keys exist +* **Update** — [Set Item With Value][], [Set Item With Key][], [Set Dictionary Item With Value][], [Find And Replace Text][] +* **Remove** — [Remove Item With Value][], [Remove Item With Key][], [Remove Dictionary Item With Value][], [Find And Remove Text][] + +Blocks that return **all** matches (for example [Get Indexes Of Items With Value][], [Find All Text][]) do not take a single `Occurrence` property; use those when you need every index. + +For how items or text are considered a match, see [Object Equality][] (lists and dictionaries) and [Equality][] (text). + +## Occurrences vs indexes + +[Indexes][] and occurrences solve different problems. This table matches the discussion on [Indexes][indexes]: + +| | **Index** | **Occurrence** | +| --- | --- | --- | +| Meaning | Position in the collection or string (0-based) | Which matching item or text segment (1-based from start, or negative from end) | +| Used when | You already know the position | Duplicates exist and you need the 1st, 2nd, or last match | +| Typical use | `($)List[2]`, [Get Item At Index][] | [Get Index Of Item With Value][] with `Occurrence` = `1` or `-1` | +| Example list `[1, 2, 1]` | Index `0` → `1`, index `2` → `1` | 1st occurrence of value `1` → index `0`; 2nd occurrence → index `2` | + +In C#, [index expressions][] can use index-from-end syntax (`^1` for the last **element**). That still refers to **position**, not to the nth **value** duplicate. Use occurrences when matching by value, key, or search text. ## Remarks -### Occurrence Not Present +### Occurrence not present + +When the requested occurrence does not exist (or there are no matches), block behaviour falls into one of three categories. Always check the individual block’s **Remarks** and **Exceptions** sections. + +| Behaviour | When it applies | Examples | +| --- | --- | --- | +| **Predetermined output** | An output property is set to a sentinel value | [Get Index Of Item With Value][] and [Get Index Of Text][] set `Index` to `-1`; [Find Text][] sets `Match` to `null` | +| **No operation** | The collection or text is left unchanged | [Remove Item With Value][], [Set Item With Value][], [Remove Item With Key][] when there is nothing to remove or set | +| **Exception** | The flow stops with an error | [Get Item With Key][], [Set Item With Key][] throw [OccurrenceNotPresentException][] | + +[OccurrenceNotPresentException][] is also thrown when `Occurrence` is `0` on blocks that use that exception (see [Occurrence is zero](#occurrence-is-zero)). + +### Occurrence is zero + +`0` cannot mean “first” or “last” match. Blocks handle it as follows: + +| Behaviour | Blocks | +| --- | --- | +| [OccurrenceNotPresentException][] | [Get Item With Key][], [Set Item With Key][] | +| Output set to “not found” (`-1` or `null`) | [Get Index Of Item With Value][], [Get Index Of Text][], [Find Text][] | +| No operation | [Remove Item With Value][], [Set Item With Value][], and similar remove/set-by-value or remove-by-key blocks | + +### Dictionaries and duplicate entries + +[Dictionary<TKey, TItem>][] blocks that support `Occurrence` may operate on: -TODO: +* **Duplicate values** — several entries whose items match the same value ([Set Dictionary Item With Value][], [Remove Dictionary Item With Value][]) +* **Duplicate keys** — several entries that share the same key instance (for example multiple `List` keys that compare equal under [Object Equality][]); [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][] -Blocks will either throw an exception if the occurrence is not present (e.g. OccurrenceNotPresentException), or they will handle the occurrence not being present either by performing no operation or by returning a predetermined value. +Unlike [List][] and [Array][], dictionaries do not have a guaranteed user-visible order. For duplicate keys or values, **which entry is the 1st or 2nd occurrence** follows the underlying .NET implementation and is not documented as stable. Prefer lists when order must be predictable. -For example: +### Matching items and text -- Get Index Of Item With Value (returns predetermined value) -- Remove Item With Value (no operation performed) -- Get Item With Key (throws OccurrenceNotPresentException) +* **Lists and dictionaries (by value)** — whether an item matches uses block [Object Equality][] rules (reference equality first, then value equality for reference types). +* **Text** — whether a segment matches uses the block’s [Comparison Type][] and [Search Options][]; see [Equality][]. ### Known Limitations @@ -51,16 +139,78 @@ None ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][items] +* [Keys][keys] +* [Indexes][indexes] +* [Object Equality][] +* [Working with Text][] ### Related Data Types -TODO +* [List<TItem>][] +* [Dictionary<TKey, TItem>][] +* [String][] +* [Int32][] ### Related Blocks -TODO +* [List][] — [Get Index Of Item With Value][], [Get Indexes Of Items With Value][], [Set Item With Value][], [Remove Item With Value][] +* [Dictionary][] — [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Set Dictionary Item With Value][], [Remove Dictionary Item With Value][] +* Text — [Find Text][], [Find All Text][], [Get Index Of Text][], [Find And Replace Text][], [Find And Remove Text][] ### External Documentation -TODO +* [Indices and ranges (C#)][MS Indices and Ranges] — index-from-end (`^n`) vs occurrence numbering +* [System.Collections.Generic.List<T>.IndexOf][MS List IndexOf] — returns the **first** 0-based index of a value (contrast with nth occurrence) +* [System.String.IndexOf][MS String IndexOf] — first match in a string +* [System.String.LastIndexOf][MS String LastIndexOf] — last match in a string + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[Working with Text]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Comparison Type]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.ComparisonTypes.MainDoc" >}} +[Search Options]: {{< url path="Cortex.Reference.DataTypes.Text.SearchOptions.MainDoc" >}} +[Text To Find]: {{< url path="Cortex.Reference.DataTypes.Text.TextToFind.MainDoc" >}} + +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} + +[Literal]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} + +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} +[Get Indexes Of Items With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexesOfItemsWithValue.MainDoc" >}} +[Set Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemWithValue.MainDoc" >}} +[Remove Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemWithValue.MainDoc" >}} +[Set Dictionary Item With Value]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithValue.MainDoc" >}} +[Remove Dictionary Item With Value]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithValue.MainDoc" >}} + +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Remove Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithKey.MainDoc" >}} + +[Find Text]: {{< url path="Cortex.Reference.Blocks.Text.FindText.FindText.MainDoc" >}} +[Find All Text]: {{< url path="Cortex.Reference.Blocks.Text.FindText.FindAllText.MainDoc" >}} +[Get Index Of Text]: {{< url path="Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText.MainDoc" >}} +[Find And Replace Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText.MainDoc" >}} +[Find And Remove Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndRemoveText.FindAndRemoveText.MainDoc" >}} + +[OccurrenceNotPresentException]: {{< url path="Cortex.Reference.Exceptions.Collections.OccurrenceNotPresentException.MainDoc" >}} + +[MS Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} +[MS List IndexOf]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IndexOfList" >}} +[MS String IndexOf]: {{< url path="MSDocs.DotNet.Api.System.String.IndexOf" >}} +[MS String LastIndexOf]: {{< url path="MSDocs.DotNet.Api.System.String.LastIndexOf" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md index 99be3b3b1..ce074aebe 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md @@ -7,102 +7,153 @@ weight: 1 # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO +A collection is a data structure that groups multiple values (called [items][]) so they can be stored, passed between blocks, and processed together in a flow. + +{{% ctx %}} supports several collection types. Each type differs in how items are ordered, how they are accessed, and whether the collection exists only for the duration of an execution or is persisted on the platform. The main types are: + +| Collection type | Typical access | Ordered | Persisted | +| --- | --- | --- | --- | +| [Dictionary][] | [Key][] | No | No | +| [Structure][] | [Key][] or property | No | No | +| [List][] | [Index][] | Yes | No | +| [Array][] | [Index][] | Yes | No | +| [Queue][] | Dequeue, peek, or enumerate | Yes (by priority, then FIFO) | No | +| [Data Storage Collection][] | [Key][] only | No | Yes | + +For more detail on [keys][], [indexes][], and [items][], see the related concept pages in this section. ## Types of Collections ### Dictionaries -TODO: +A [Dictionary<TKey, TItem>][] is a collection of key/item pairs based on [System.Collections.Generic.Dictionary<TKey, TItem>][]. + +* `TKey` is the data type of each [key][] used to access an item. +* `TItem` is the data type of each [item][items] stored in the collection. + +Each item is accessed by its key. In the [Expression Editor][], dictionary items are accessed using [index expressions][] (for example `dictionary["StringKey1"]`). + +#### Homogenous and heterogenous dictionaries + +A dictionary is [homogenous][] when every item has the same data type (for example `Dictionary`). It is [heterogenous][] when items may have different data types (for example `Dictionary`). -- Talk about typed dictionaries (TKey, TItem) -- Talk about heterogenous vs homogenous dictionaries (fix single data type vs multiple data types links in all dictionary blocks) -- Talk about keys, uniqueness and equality (link to Keys page) -- Talk about complex keys (link to Keys page) -- Items can be accessed as indexes -- Link to Data Type -- Creating a Dictionary +For more examples of creating dictionaries with literal and expression syntax, see [Create a Dictionary<TKey, TItem>][] on the [Dictionary<TKey, TItem>][] data type page. -#### Accessing Items +#### Keys + +Each key in a dictionary must be unique. Keys cannot be `null`. How equality is determined depends on whether you use C# syntax or dictionary blocks; see [Keys][]. + +Keys whose data type is not a simple scalar (complex keys) are supported for some `TKey` types; see the Known Limitations section on [Dictionary<TKey, TItem>][] for supported key types and display limitations in Gateway. + +#### Accessing items ##### Keys +Use a [key][] in an [index expression][index expressions] or pass the key to a [Dictionary][] block property. Dictionary blocks that add, set, or remove items document key equality in their remarks. + ### Structures -TODO: +A [Structure][] is a {{% ctx %}} collection type (`Cortex.DataTypes.Dictionaries.Structure`) that represents key/item pairs where keys are always [String][] and items may be any data type. It implements [IDictionary<TKey, TItem>][] (effectively `IDictionary`) and behaves like a [heterogenous][] dictionary with string keys. -- Structures are IDictionary<string, object> underlying (how does this interact with blocks?) - - Talk about {} and dynamic vs object -- Talk about keys, uniqueness and equality (link to Keys page) -- Items can be accessed as indexes or properties -- Link to Data Type -- Creating a Structure +In the [Expression Editor][]: -#### Accessing Items +* A JSON object literal such as `{ "Name": "value", "Count": 1 }` creates a [Structure][], not a [Dictionary][] (dictionary literal syntax is not supported). +* Items can be accessed with [property expressions][] (for keys that follow C# identifier rules) or [index expressions][]. + +For creation examples, see [Create a Structure][] on the [Structure][] data type page. + +#### Accessing items ##### Properties +When the key is a valid C# identifier, use property syntax (for example `structure.Name` or `structure.Count`). See [Property expressions][] in the [Expression Editor][] documentation. + ##### Keys +Any string key can be used with index syntax (for example `structure["any-key"]`). Keys must be unique and cannot be `null`; see [Keys][]. + ### Lists -TODO: +A [List<TItem>][] is an ordered collection based on [System.Collections.Generic.List<TItem>][]. `TItem` is the data type of each [item][items]. -- Talk about typed dictionaries (TItem) -- Talk about [] and dynamic vs object -- Talk about typed lists -- Talk about heterogenous vs homogenous lists (fix single data type vs multiple data types links in all list blocks) -- Items can be accessed as indexes -- Link to Data Type -- Creating a List +Lists can be created with `[]` literal syntax or `new List()` expressions. Using `[]` without other context creates a `List`. See [Create a List<TItem>][] on the [List<TItem>][] data type page. -#### Accessing Items +#### Homogenous and heterogenous lists + +A list is [homogenous][] when every item has the same data type (for example `List` or `List`). It is [heterogenous][] when items may have different data types (for example `List` or `List`). + +List blocks refer to these as lists containing items of a single data type versus multiple data types. + +#### Accessing items ##### Indexes +Each item is accessed by a zero-based [index][]. Indexes are used in [index expressions][] in the Expression Editor and in [List][] block properties. See [Indexes][]. + ### Queues -TODO: +A [QueueWithPriority<TItem, TPriority>][] is a {{% ctx %}} queue type that orders items by [TPriority][], similar to [System.Collections.Generic.PriorityQueue<TElement, TPriority>][], while preserving first-in-first-out order among items that share the same priority. + +* Items with a lower priority value are dequeued before items with a higher priority value (as determined by the queue's priority comparer; see [QueueWithPriority<TItem, TPriority>][]). +* Among items with the same priority, the item that was enqueued first is dequeued first. + +`TItem` is the data type of each queued item. Queues are typically created with `new QueueWithPriority(...)`. See [QueueWithPriority<TItem, TPriority>][] for creation examples. + +#### Accessing items + +Items are not accessed by index or key. Use [Queue][] blocks to: -- Talk about first-in-first-out -- Talk about priority and order items in a queue -- Talk about typed queues -- Talk about how items can be accessed (e.g. dequeue/peek or get list of items) -- Link to Data Type -- Creating a queue +* [Enqueue][] items +* [Dequeue][] or [Peek][] the next item (without removing it when peeking) +* [Get count of all items][] or peek for inspection without changing queue order ### Data Storage Collection -TODO: +A Data Storage Collection is a named, persisted collection of key/data pairs stored by the [Data Storage Service][] for use across flow executions within a defined [collection scope][]. -- Talk about persistence -- Talk about about heterogenous property (takes any data type and can not be restricted) -- Talk about keys and uniqueness of keys (link to keys page) -- Items can only be accessed via key -- Mention high availability? -- Mention api access? +Unlike in-memory [Dictionary][] or [Structure][] variables: + +* Data survives after the execution that wrote it ends (subject to platform configuration and service availability). +* Each collection is identified by a [collection scope][] and [collection name][]. +* Each entry has a [String][] [key][] and a [dynamic][] `Data` value, so stored data is always [heterogenous][] and cannot be restricted to a single item type at the property level. +* Items are read and written only by [key][] using [Data Storage][] blocks (for example [Create Collection][], [Write Data With Key][], [Read Data With Key][]). + +Keys must be unique within a collection, cannot be `null`, and are case sensitive. Collection names are case insensitive within a scope. See [Keys][] and the [Write Data With Key][] block for key and naming behaviour. + +Operations may throw [ServiceDoesNotExistException][] or [ServiceUnavailableException][] when the Data Storage Service is not deployed or not healthy. ## Arrays vs Lists +[Array][] and [List<TItem>][] are both ordered collections that use zero-based [indexes][]. They are closely related in C# but behave differently in important ways. + ### Differences -### When To Use Arrays +| | [Array][] | [List<TItem>][] | +| --- | --- | --- | +| Size | Fixed when created | Grows and shrinks | +| Add or remove items | Not supported | Supported (blocks and C# methods) | +| Typical declaration | `new Int32[] { 1, 2, 3 }` | `new List() { 1, 2, 3 }` or `[1, 2, 3]` | +| Common in flow blocks | Rarely required as a property type | Widely used | + +See [Array][] and [List<TItem>][] for creation examples and interface support. + +### When to use arrays + +Use an array when the number of items is fixed and will not change during processing—for example, a predefined set of values returned from an API or a fixed batch size. -### When To Use Lists +### When to use lists -- TODO: Some info on when to use array vs list - https://www.educba.com/c-sharp-list-vs-array/ +Use a list when items are added, removed, or reordered during a flow, or when the count is not known in advance. Most [List][] blocks and literal `[]` syntax expect [List<TItem>][], not arrays. ## Remarks ### Known Limitations -#### Complex Keys do not show sho correctly in the Variable Details Viewer +#### Complex keys do not show correctly in the Variable Details Viewer -Currently, if a Dictionary is shown in the Variable Details Viewer and contains Complex Data types as its keys, the data within the variable will not be displayed correctly. +Currently, if a [Dictionary][] is shown in the Variable Details Viewer and contains complex data types as its keys, the data within the variable will not be displayed correctly. In the future this limitation may be removed. @@ -110,16 +161,98 @@ In the future this limitation may be removed. ### Related Concepts -TODO +* [Collections][] +* [Keys][] +* [Indexes][] +* [Items][] +* [Occurrences][] +* [Generics][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [List<TItem>][] +* [Array][] +* [QueueWithPriority<TItem, TPriority>][] +* [IDictionary<TKey, TItem>][] +* [IList<TItem>][] +* [IEnumerable][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][]) +* [List][] blocks (for example [Add Item At End][]) +* [Queue][] blocks (for example [Enqueue Item][], [Dequeue Item][], [Peek Item][]) +* [Data Storage][] blocks (for example [Create Collection][], [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [System.Collections.Generic.IDictionary<TKey, TItem>][MS IDictionary] +* [System.Collections.Generic.PriorityQueue<TElement, TPriority>][MS PriorityQueue] + +[items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[homogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Homogenous" >}} +[heterogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Heterogenous" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Queue]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[IEnumerable]: {{< url path="Cortex.Reference.DataTypes.Collections.IEnumerable.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[TPriority]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} +[Create a Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.CreateNew" >}} +[Create a List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.CreateNew" >}} +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} + +[Data Storage Collection]: {{< ref "#data-storage-collection" >}} +[Data Storage Service]: {{< url path="Cortex.Guides.CortexInnovation.CoreApplication.Services.DataStorageService.MainDoc" >}} +[collection scope]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[collection name]: {{< url path="Cortex.Reference.Blocks.DataStorage.CreateCollection.CreateCollectionBlock.MainDoc" >}} + +[Create Collection]: {{< url path="Cortex.Reference.Blocks.DataStorage.CreateCollection.CreateCollectionBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} +[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd.MainDoc" >}} +[Enqueue Item]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue Item]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek Item]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Enqueue]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Get count of all items]: {{< url path="Cortex.Reference.Blocks.Queues.GetCount.GetCountOfAllItemsBlock.MainDoc" >}} + +[ServiceDoesNotExistException]: {{< url path="Cortex.Reference.Exceptions.Services.ServiceDoesNotExistException.MainDoc" >}} +[ServiceUnavailableException]: {{< url path="Cortex.Reference.Exceptions.Services.ServiceUnavailableException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS IDictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IDictionary" >}} +[MS PriorityQueue]: {{< url path="MSDocs.DotNet.Api.System.PriorityQueue.MainDoc" >}} +[System.Collections.Generic.Dictionary<TKey, TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[System.Collections.Generic.PriorityQueue<TElement, TPriority>]: {{< url path="MSDocs.DotNet.Api.System.PriorityQueue.MainDoc" >}} diff --git a/data/urls.toml b/data/urls.toml index 78c01b9d3..02eaa1864 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -1006,7 +1006,7 @@ LdapAuth = "/docs/guides/user-guides/user-interfaces/gateway/admin/authentication/#ldap" OidcAuth = "/docs/guides/user-guides/user-interfaces/gateway/admin/authentication/#openid-connect" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Authorisation] - MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/authorisation/" + MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/authorisation/" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Packages] MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/packages/" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Packages.Overview] @@ -1489,6 +1489,9 @@ MainDoc = "/docs/reference/blocks/queues/dequeue-item/dequeue-item-block-2" [Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemsBlock] MainDoc = "/docs/reference/blocks/queues/dequeue-item/dequeue-items-block-2" + [Cortex.Reference.Blocks.Queues.GetCount] + [Cortex.Reference.Blocks.Queues.GetCount.GetCountOfAllItemsBlock] + MainDoc = "/docs/reference/blocks/queues/get-count/get-count-of-all-items-block-2" [Cortex.Reference.Blocks.Queues.EnqueueItem] [Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock] MainDoc = "/docs/reference/blocks/queues/enqueue-item/enqueue-item-block-2" @@ -1562,6 +1565,17 @@ [Cortex.Reference.Blocks.Text.DecodeText] [Cortex.Reference.Blocks.Text.DecodeText.DecodeText] MainDoc = "/docs/reference/blocks/text/decode-text/decode-text-block" + [Cortex.Reference.Blocks.Text.FindText] + [Cortex.Reference.Blocks.Text.FindText.FindAllText] + MainDoc = "/docs/reference/blocks/text/find-text/find-all-text-block" + [Cortex.Reference.Blocks.Text.FindText.FindText] + MainDoc = "/docs/reference/blocks/text/find-text/find-text-block" + [Cortex.Reference.Blocks.Text.FindAndRemoveText] + [Cortex.Reference.Blocks.Text.FindAndRemoveText.FindAndRemoveText] + MainDoc = "/docs/reference/blocks/text/find-and-remove-text/find-and-remove-text-block" + [Cortex.Reference.Blocks.Text.FindAndReplaceText] + [Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText] + MainDoc = "/docs/reference/blocks/text/find-and-replace-text/find-and-replace-text-block" [Cortex.Reference.Blocks.Text.FormatText] [Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue] MainDoc = "/docs/reference/blocks/text/format-text/format-text-with-value-block-1/" @@ -1569,6 +1583,9 @@ [Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues] MainDoc = "/docs/reference/blocks/text/format-text/format-text-with-values-block-1/" FormatTemplate = "/docs/reference/blocks/text/format-text/format-text-with-values-block-1/#format-template" + [Cortex.Reference.Blocks.Text.GetIndex] + [Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText] + MainDoc = "/docs/reference/blocks/text/get-index/get-index-of-text-block/" [Cortex.Reference.Blocks.Text.GetText] [Cortex.Reference.Blocks.Text.GetText.GetTextAtBeginning] MainDoc = "/docs/reference/blocks/text/get-text/get-text-at-beginning-block/" @@ -1757,6 +1774,7 @@ [Cortex.Reference.Concepts.WorkingWith.Collections] MainDoc = "/docs/reference/concepts/working-with/collections/" [Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection] + MainDoc = "/docs/reference/concepts/working-with/collections/what-is-a-collection" DataStorage = "/docs/reference/concepts/working-with/collections/what-is-a-collection/#data-storage-collection" ArraysVersusLists = "/docs/reference/concepts/working-with/collections/what-is-a-collection/#arrays-vs-lists" [Cortex.Reference.Concepts.WorkingWith.Collections.Items] @@ -1820,6 +1838,8 @@ MainDoc = "/docs/reference/concepts/working-with/objects/object-equality/" [Cortex.Reference.Concepts.WorkingWith.Scopes] MainDoc = "/docs/reference/concepts/working-with/scopes/" + [Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope] + MainDoc = "/docs/reference/concepts/working-with/scopes/" [Cortex.Reference.Concepts.WorkingWith.Tasks] MainDoc = "/docs/reference/concepts/working-with/tasks/" [Cortex.Reference.Concepts.WorkingWith.Text] @@ -2887,6 +2907,7 @@ Dictionary = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2" IEnumerable_TItem = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1" IList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1" + IndexOfList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.indexof#system-collections-generic-list-1.indexof" List = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1" IReadOnlyList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlylist-1" IReadOnlyCollection = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlycollection-1" @@ -3010,8 +3031,10 @@ ConcatOperator = "https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#-and--operators" StringInterpolation = "https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#string-interpolation" Format = "https://learn.microsoft.com/en-us/dotnet/api/system.string.format" + IndexOf = "https://learn.microsoft.com/en-us/dotnet/api/system.string.indexof" Insert = "https://learn.microsoft.com/en-us/dotnet/api/system.string.insert" Join = "https://learn.microsoft.com/en-us/dotnet/api/system.string.join" + LastIndexOf = "https://learn.microsoft.com/en-us/dotnet/api/system.string.lastindexof" [MSDocs.DotNet.Api.System.TimeSpan] MainDoc = "https://learn.microsoft.com/en-us/dotnet/api/system.timespan" [MSDocs.DotNet.Api.System.Globalization]