Version dev

# Arrays und Maps

Der speedata Publisher unterstützt Arrays und Maps als native Datenstrukturen. Damit lassen sich geordnete Listen und Schlüssel-Wert-Zuordnungen direkt in XPath-Ausdrücken verwenden – ohne den Umweg über dynamisch erzeugte Variablennamen.

Die Konstruktoren (`[...]`, `array { ... }`, `map { ... }`) und der Lookup-Operator (`?`) sind direkt verwendbar. Für die Funktionen wie `array:size()`, `map:keys()` usw. müssen die entsprechenden Namensräume im Wurzelelement deklariert werden:

    <Layout xmlns="urn:speedata.de:2009/publisher/en"
        xmlns:sd="urn:speedata:2009/publisher/functions/en"
        xmlns:array="http://www.w3.org/2005/xpath-functions/array"
        xmlns:map="http://www.w3.org/2005/xpath-functions/map">

Die Präfixe `array` und `map` sind frei wählbar, die Namensraum-URIs müssen aber genau so angegeben werden.

## Arrays

Ein Array ist eine geordnete Folge von Werten. Jedes Element (»Member«) kann ein beliebiger XPath-Wert sein - Zahlen, Zeichenketten oder sogar weitere Arrays und Maps.

### Array erzeugen

Eckige Klammern - jeder Ausdruck wird zu einem Member:

    <SetVariable variable="farben" select="['rot', 'grün', 'blau']"/>
    <SetVariable variable="zahlen" select="[10, 20, 30]"/>
    <SetVariable variable="leer" select="[]"/>

Curly-Konstruktor - eine Sequenz wird in einzelne Members aufgeteilt:

    <SetVariable variable="eins_bis_fünf" select="array { 1 to 5 }"/>

Das Ergebnis ist ein Array mit fünf Members: 1, 2, 3, 4, 5.

### Auf Array-Elemente zugreifen

Der Lookup-Operator `?` greift per Index (1-basiert) auf ein Member zu:

    <Value select="$farben?1"/>   <!-- ergibt: rot -->
    <Value select="$farben?3"/>   <!-- ergibt: blau -->

Mit dem Wildcard `?*` erhält man alle Members als flache Sequenz:

    <Value select="string-join($farben?*, ', ')"/>
    <!-- ergibt: rot, grün, blau -->

### Array-Funktionen

Alle Array-Funktionen befinden sich im Namensraum `array:`. Arrays sind unveränderlich – Funktionen wie `array:put()` oder `array:append()` geben ein neues Array zurück, das Original bleibt unverändert.

`array:size(<Array>)`
  Anzahl der Members. `array:size([1, 2, 3])` ergibt `3`.
`array:get(<Array>, <Position>)`
  Gibt das Member an der Position zurück. `array:get(['a', 'b', 'c'], 2)` ergibt `b`.
`array:append(<Array>, <Wert>)`
  Neues Array mit angehängtem Member. `array:append([1, 2], 3)` ergibt `[1, 2, 3]`.
`array:put(<Array>, <Position>, <Wert>)`
  Neues Array mit ersetztem Member an der Position.
`array:remove(<Array>, <Position>)`
  Neues Array ohne das Member an der Position.
`array:subarray(<Array>, <Start>[, <Länge>])`
  Teilarray ab der Startposition.
`array:join(<Array-Sequenz>)`
  Verbindet eine Sequenz von Arrays zu einem einzigen Array.
`array:flatten(<Array>)`
  Löst verschachtelte Arrays rekursiv auf und gibt eine flache Sequenz zurück. `array:flatten([1, [2, 3]])` ergibt die Sequenz `1, 2, 3`.

## Maps

Eine Map ist eine Sammlung von Schlüssel-Wert-Paaren. Schlüssel sind atomare Werte (typischerweise Zeichenketten oder Zahlen), Werte können beliebig sein.

### Map erzeugen

    <SetVariable variable="preise"
        select="map { 'Apfel': 1.50, 'Birne': 2.30, 'Kirsche': 4.90 }"/>
    <SetVariable variable="leer" select="map {}"/>

Schlüssel und Wert werden durch `:` getrennt, Einträge durch `,`.

### Auf Map-Einträge zugreifen

Der Lookup-Operator `?` greift per Schlüsselname zu:

    <Value select="$preise?Apfel"/>   <!-- ergibt: 1.5 -->
    <Value select="$preise?Kirsche"/> <!-- ergibt: 4.9 -->

Mit dem Wildcard `?*` erhält man alle Werte:

    <Value select="count($preise?*)"/>  <!-- ergibt: 3 -->

### Map-Funktionen

Alle Map-Funktionen befinden sich im Namensraum `map:` und geben neue Maps zurück.

`map:size(<Map>)`
  Anzahl der Einträge. `map:size(map { 'a': 1, 'b': 2 })` ergibt `2`.
`map:keys(<Map>)`
  Gibt die Schlüssel als Sequenz zurück.
`map:get(<Map>, <Schlüssel>)`
  Gibt den Wert zum Schlüssel zurück. Leere Sequenz, wenn der Schlüssel nicht existiert.
`map:contains(<Map>, <Schlüssel>)`
  `true()` wenn der Schlüssel vorhanden ist.
`map:put(<Map>, <Schlüssel>, <Wert>)`
  Neue Map mit eingefügtem oder ersetztem Eintrag.
`map:remove(<Map>, <Schlüssel>)`
  Neue Map ohne den Eintrag.
`map:merge(<Map-Sequenz>)`
  Führt eine Sequenz von Maps zusammen. Bei doppelten Schlüsseln gewinnt der letzte Wert.
`map:entry(<Schlüssel>, <Wert>)`
  Erzeugt eine Map mit einem einzigen Eintrag.

## Praxisbeispiele

### Produktkatalog mit Preisen

    <SetVariable variable="preise" select="map {
        'SKU-001': 29.95,
        'SKU-002': 79.00,
        'SKU-003': 149.90
    }"/>

    <Record element="product">
        <PlaceObject>
            <Textblock>
                <Paragraph>
                    <Value select="@name || ': ' || $preise?(@sku) || ' €'"/>
                </Paragraph>
            </Textblock>
        </PlaceObject>
    </Record>

### Farbzuordnung

    <SetVariable variable="farben" select="map {
        'fehler': 'rot',
        'warnung': 'gelb',
        'ok': 'grün'
    }"/>

    <Value select="$farben?($status)"/>

### Sammlung aufbauen

Da Arrays und Maps unveränderlich sind, baut man Sammlungen schrittweise auf:

    <SetVariable variable="einträge" select="[]"/>

    <SetVariable variable="einträge"
        select="array:append($einträge, 'Erster Eintrag')"/>
    <SetVariable variable="einträge"
        select="array:append($einträge, 'Zweiter Eintrag')"/>

#### Inhalt