RDF List Transformation Operations

Open inAnthropic

This guide covers operations for transforming and reordering RDF lists. These operations modify the order of elements without adding or removing them.

What You'll Learn

  • How to swap elements at different positions
  • How to reverse an entire list
  • Common use cases for list reordering

Operations Summary

OperationDescriptionTime Complexity
rdflist_swapExchange elements at two positionsO(n)
rdflist_reverseReverse the entire list in-placeO(n)

rdflist_swap

Exchanges the values at two positions in the list. Both positions use 0-based indexing.

Syntax

Example: JavaScript
WOQL.lib().rdflist_swap(consSubject, positionA, positionB)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier
positionAnumberFirst position (0-based)
positionBnumberSecond position (0-based)

Examples

Example: JavaScript
// List: [A, B, C, D]

// Swap first and last: [D, B, C, A]
await client.query(WOQL.lib().rdflist_swap(listHead, 0, 3));

// Swap adjacent elements: [A, C, B, D]
await client.query(WOQL.lib().rdflist_swap(listHead, 1, 2));

// Swap middle with first: [C, B, A, D]
await client.query(WOQL.lib().rdflist_swap(listHead, 2, 0));

No-Op When Positions Are Equal

Example: JavaScript
// Swapping position 1 with itself does nothing
await client.query(WOQL.lib().rdflist_swap(listHead, 1, 1));
// List remains unchanged

Complete Swap Example with Documents

This example creates a schema with a document containing an ordered list, then swaps elements.

Example: JavaScript
// Step 1: Define schema with a document that has an ordered list
const schema = [
  {
    "@type": "Class",
    "@id": "Playlist",
    "@key": { "@type": "Lexical", "@fields": ["name"] },
    "name": "xsd:string",
    "songs": { "@type": "List", "@class": "xsd:string" }
  }
];

await client.addDocument(schema, { graph_type: "schema" });

// Step 2: Create a playlist document with songs
const playlist = {
  "@type": "Playlist",
  "name": "Road Trip",
  "songs": ["Highway Star", "Born to Run", "Life is a Highway", "On the Road Again"]
};

await client.addDocument(playlist);

// Step 3: Get the list head from the document
const getListHead = WOQL.triple("Playlist/Road%20Trip", "songs", "v:listHead");
const headResult = await client.query(getListHead);
const listHead = headResult.bindings[0]["listHead"];

// Step 4: Swap songs - move "Life is a Highway" (pos 2) to first position (pos 0)
// Before: ["Highway Star", "Born to Run", "Life is a Highway", "On the Road Again"]
await client.query(WOQL.lib().rdflist_swap(listHead, 2, 0));
// After:  ["Life is a Highway", "Born to Run", "Highway Star", "On the Road Again"]

// Step 5: Verify the result
const readResult = await client.query(
  WOQL.lib().rdflist_list(listHead, "v:songs")
);
const songs = readResult.bindings[0]["songs"].map(s => s["@value"]);
console.log(songs);
// ["Life is a Highway", "Born to Run", "Highway Star", "On the Road Again"]

// The document now reflects the swapped order
const updatedPlaylist = await client.getDocument({ id: "Playlist/Road%20Trip" });
console.log(updatedPlaylist.songs);
// ["Life is a Highway", "Born to Run", "Highway Star", "On the Road Again"]

Use Cases

  • Sorting: Implement sorting algorithms
  • Reordering: Move items to different positions
  • User-driven ordering: Respond to drag-and-drop operations
  • Shuffling: Randomize list order

rdflist_reverse

Reverses the order of all elements in a list. This is an in-place operation that modifies the list structure.

Syntax

Example: JavaScript
WOQL.lib().rdflist_reverse(consSubject)

Parameters

ParameterTypeDescription
consSubjectstringThe list head identifier

Examples

Example: JavaScript
// 4-element list: [A, B, C, D] → [D, C, B, A]
await client.query(WOQL.lib().rdflist_reverse(listHead));

// 2-element list: [X, Y] → [Y, X]
await client.query(WOQL.lib().rdflist_reverse(listHead));

// Single-element list: [Only] → [Only] (unchanged)
await client.query(WOQL.lib().rdflist_reverse(listHead));

Complete Reverse Example

Example: JavaScript
// Create list: [A, B, C, D]
const createQuery = WOQL.and(
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell1"),
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell2"),
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell3"),
  WOQL.idgen_random("terminusdb://data/Cons/", "v:cell4"),
  WOQL.add_triple("v:cell1", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell1", "rdf:first", WOQL.string("A")),
  WOQL.add_triple("v:cell1", "rdf:rest", "v:cell2"),
  WOQL.add_triple("v:cell2", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell2", "rdf:first", WOQL.string("B")),
  WOQL.add_triple("v:cell2", "rdf:rest", "v:cell3"),
  WOQL.add_triple("v:cell3", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell3", "rdf:first", WOQL.string("C")),
  WOQL.add_triple("v:cell3", "rdf:rest", "v:cell4"),
  WOQL.add_triple("v:cell4", "rdf:type", "rdf:List"),
  WOQL.add_triple("v:cell4", "rdf:first", WOQL.string("D")),
  WOQL.add_triple("v:cell4", "rdf:rest", "rdf:nil")
);

const result = await client.query(createQuery);
const listHead = result.bindings[0]["cell1"];

// Reverse the list in place
await client.query(WOQL.lib().rdflist_reverse(listHead));

// Read the reversed list using path query with ordering
const readQuery = WOQL.and(
  WOQL.path(listHead, "rdf:rest*", "v:node", "v:path"),
  WOQL.length("v:path", "v:pos"),
  WOQL.triple("v:node", "rdf:first", "v:val")
);
const readResult = await client.query(WOQL.order_by("v:pos", readQuery));

const values = readResult.bindings
  .map(b => b["val"]["@value"])
  .filter(v => v !== undefined);
console.log(values); // ["D", "C", "B", "A"]

Edge Cases

Example: JavaScript
// Reversing a 2-element list
// [X, Y] → [Y, X]
await client.query(WOQL.lib().rdflist_reverse(twoElementList));

// Reversing a single-element list (no change)
// [Only] → [Only]
await client.query(WOQL.lib().rdflist_reverse(singleElementList));

// Reversing an empty list (no-op)
// rdf:nil → rdf:nil

Use Cases

  • Display order: Show newest first vs oldest first
  • Undo stack: Process items in reverse order
  • Algorithm implementation: Part of various algorithms
  • Data transformation: Prepare data for different views

Reversing After Pushes

Example: JavaScript
// Build list by pushing (results in reverse order)
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("C")));
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("B")));
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("A")));
// List is now: [A, B, C] (pushed in reverse order)

// Or push in natural order and reverse
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("A")));
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("B")));
await client.query(WOQL.lib().rdflist_push(listHead, WOQL.string("C")));
// List is: [C, B, A]

await client.query(WOQL.lib().rdflist_reverse(listHead));
// List is now: [A, B, C]

Complete Example: Priority Reordering

Example: JavaScript
import { WOQLClient, WOQL } from "@terminusdb/terminusdb-client";

async function priorityReorder(client) {
  // Create a task list: [Task1, Task2, Task3, Task4]
  const createQuery = WOQL.and(
    WOQL.idgen_random("terminusdb://data/Cons/", "v:cell1"),
    WOQL.idgen_random("terminusdb://data/Cons/", "v:cell2"),
    WOQL.idgen_random("terminusdb://data/Cons/", "v:cell3"),
    WOQL.idgen_random("terminusdb://data/Cons/", "v:cell4"),
    WOQL.add_triple("v:cell1", "rdf:type", "rdf:List"),
    WOQL.add_triple("v:cell1", "rdf:first", WOQL.string("Low Priority")),
    WOQL.add_triple("v:cell1", "rdf:rest", "v:cell2"),
    WOQL.add_triple("v:cell2", "rdf:type", "rdf:List"),
    WOQL.add_triple("v:cell2", "rdf:first", WOQL.string("Medium Priority")),
    WOQL.add_triple("v:cell2", "rdf:rest", "v:cell3"),
    WOQL.add_triple("v:cell3", "rdf:type", "rdf:List"),
    WOQL.add_triple("v:cell3", "rdf:first", WOQL.string("High Priority")),
    WOQL.add_triple("v:cell3", "rdf:rest", "v:cell4"),
    WOQL.add_triple("v:cell4", "rdf:type", "rdf:List"),
    WOQL.add_triple("v:cell4", "rdf:first", WOQL.string("Urgent")),
    WOQL.add_triple("v:cell4", "rdf:rest", "rdf:nil")
  );
  
  const result = await client.query(createQuery);
  const listHead = result.bindings[0]["cell1"];
  
  console.log("Initial order:");
  await printList(client, listHead);
  // [Low Priority, Medium Priority, High Priority, Urgent]
  
  // Move Urgent to the front by swapping with position 0
  await client.query(WOQL.lib().rdflist_swap(listHead, 3, 0));
  
  console.log("After moving Urgent to front:");
  await printList(client, listHead);
  // [Urgent, Medium Priority, High Priority, Low Priority]
  
  // Reverse to show low priority first
  await client.query(WOQL.lib().rdflist_reverse(listHead));
  
  console.log("After reversing (low priority first):");
  await printList(client, listHead);
  // [Low Priority, High Priority, Medium Priority, Urgent]
}

async function printList(client, listHead) {
  const query = WOQL.lib().rdflist_list(listHead, "v:items");
  const result = await client.query(query);
  const items = result.bindings[0]["items"].map(i => i["@value"]);
  console.log(items);
}

Performance Considerations

  • Swap requires traversing to both positions, so swapping elements near the end is slower
    • If the values to swap are known and unique, it is faster to identify the Cons from from the values and swap (remove and add) the rdf:first triples from each cons.
  • Reverse traverses the entire list once, making it O(n)
  • For frequent reordering of large lists, consider using a different data structure

Read More

Was this helpful?