T4.1: Traverses and Sorts Arrays

Knowledge Review - InterSystems ObjectScript Specialist

1. Ordenación de subíndices (orden numérico canónico)

Puntos Clave

  • Orden numérico canónico: Los subíndices numéricos se ordenan por valor numérico: -2, 0, 1, 10
  • Orden de cadenas: Los subíndices no numéricos se ordenan en orden de colación ASCII después de todos los numéricos
  • Subíndices mixtos: Los numéricos van primero, luego las cadenas: -2, 0, 1, 10, "A", "B", "a"
  • Forma canónica: Un número es canónico si es igual a su propia interpretación numérica (sin ceros a la izquierda, sin ceros finales después del decimal)
  • Cadena vacía: La cadena vacía "" se ordena antes de todos los demás subíndices
  • Sensibilidad a mayúsculas: Las letras mayúsculas se ordenan antes que las minúsculas ("A" < "B" < "a" < "b")

Notas Detalladas

Descripción general

InterSystems IRIS almacena los subíndices de globals y arrays locales en un orden de colación bien definido. Comprender este orden es esencial para recorrer arrays de manera predecible. La colación predeterminada coloca los valores numéricos canónicos primero (ordenados por valor numérico), seguidos de los valores de cadena no numéricos (ordenados por orden de bytes ASCII/Unicode).

Orden numérico canónico

Un subíndice se trata como numérico si está en forma canónica, lo que significa que es igual al resultado de sumarle cero. Por ejemplo, 1, -2, 3.14 y 0 son canónicos, mientras que "01", "3.0" y "+5" no lo son (estos se tratan como cadenas). Los numéricos canónicos se ordenan por su valor numérico, por lo que el orden es: -2, -1, 0, 0.5, 1, 2, 10, 100.

 // Demonstrate subscript ordering
 SET data(-2) = "neg two"
 SET data(0) = "zero"
 SET data(1) = "one"
 SET data(10) = "ten"
 SET data("A") = "letter A"
 SET data("B") = "letter B"
 SET data("a") = "letter a"

 // Traversing will yield: -2, 0, 1, 10, "A", "B", "a"
 SET key = ""
 FOR {
     SET key = $ORDER(data(key))
     QUIT:key=""
     WRITE key, " = ", data(key), !
 }

Colación de cadenas

Los subíndices no canónicos se almacenan como cadenas y se ordenan por sus valores de byte. Esto significa que las letras ASCII mayúsculas (A-Z, códigos 65-90) se ordenan antes que las letras minúsculas (a-z, códigos 97-122). Las cadenas que parecen números pero no son canónicas ("01", "3.0", "+5") se ordenan como cadenas, no como números.

 // "01" is NOT canonical numeric -- it sorts as a string
 SET arr(1) = "canonical one"
 SET arr("01") = "string zero-one"

 // Both nodes exist separately!
 // $ORDER yields: 1, then "01"
 SET key = ""
 FOR {
     SET key = $ORDER(arr(key))
     QUIT:key=""
     WRITE key, " -> ", arr(key), !
 }
 // Output: 1 -> canonical one
 //         01 -> string zero-one

Implicaciones prácticas

Al diseñar globals, elija los subíndices cuidadosamente. Si necesita una ordenación numérica predecible, asegúrese de que los valores sean canónicos. Si mezcla tipos, tenga en cuenta que -3, 0, 5 se ordenan primero, luego siguen "Apple", "Banana", "apple". Este orden es consistente en arrays locales, globals privados de proceso y globals persistentes.

2. Recorrido de subconjuntos de subíndices con $ORDER

Puntos Clave

  • $ORDER(array(subscript)): Devuelve el siguiente subíndice al mismo nivel después del subíndice dado
  • Inicio con cadena vacía: Use `""` como subíndice inicial para obtener el primer subíndice
  • Terminación del bucle: $ORDER devuelve `""` cuando no existen más subíndices
  • Parámetro de dirección: $ORDER(array(subscript), direction) donde 1=adelante (predeterminado), -1=reverso
  • Valor por referencia: $ORDER(array(subscript), direction, .datavalue) recupera datos en una sola llamada
  • Subconjunto de subíndices: Combine $ORDER con un punto de inicio para recorrer una porción de un array

Notas Detalladas

Descripción general

$ORDER es la función fundamental para recorrer arrays en ObjectScript. Devuelve el siguiente subíndice al mismo nivel, permitiendo la iteración a través de todos los nodos de un global o array local. La función es eficiente porque sigue directamente la estructura interna del B-tree.

Bucle básico con $ORDER

El patrón estándar comienza con una cadena vacía y hace un bucle hasta que $ORDER devuelve una cadena vacía:

 // Basic forward traversal
 SET ^colors(1) = "Red"
 SET ^colors(2) = "Green"
 SET ^colors(5) = "Blue"
 SET ^colors(10) = "Yellow"

 SET key = ""
 FOR {
     SET key = $ORDER(^colors(key))
     QUIT:key=""
     WRITE "Key: ", key, " Value: ", ^colors(key), !
 }
 // Output: Key: 1 Value: Red
 //         Key: 2 Value: Green
 //         Key: 5 Value: Blue
 //         Key: 10 Value: Yellow

Inicio desde un punto específico

Puede iniciar el recorrido desde cualquier subíndice -- $ORDER devuelve el SIGUIENTE subíndice después del que usted proporciona:

 // Start traversing from subscript 3 (gets next one: 5)
 SET key = 3
 FOR {
     SET key = $ORDER(^colors(key))
     QUIT:key=""
     WRITE key, " = ", ^colors(key), !
 }
 // Output: 5 = Blue
 //         10 = Yellow

Recorrido inverso con dirección -1

Pase -1 como segundo argumento para recorrer en orden inverso:

 // Reverse traversal
 SET key = ""
 FOR {
     SET key = $ORDER(^colors(key), -1)
     QUIT:key=""
     WRITE key, " = ", ^colors(key), !
 }
 // Output: 10 = Yellow
 //         5 = Blue
 //         2 = Green
 //         1 = Red

Recuperación de datos con el tercer argumento

El tercer argumento (pasado por referencia) recupera el valor de datos del nodo en la misma llamada, evitando una referencia global separada:

 // Efficient: get key AND value in one operation
 SET key = ""
 FOR {
     SET key = $ORDER(^colors(key), 1, value)
     QUIT:key=""
     WRITE key, " = ", value, !
 }

Esto es más eficiente para globals porque evita una segunda búsqueda en disco/caché por iteración.

Referencias de Documentación

3. Recorrido multinivel con $ORDER

Puntos Clave

  • Arrays multinivel: Los globals a menudo tienen múltiples niveles de subíndices: ^data(nivel1, nivel2, nivel3)
  • Bucles anidados: Use un bucle $ORDER por cada nivel de subíndice
  • $ORDER en cada nivel: Cada bucle interno comienza con "" y recorre todos los subíndices en ese nivel
  • Recorrido inverso: Use dirección -1 en cualquier nivel para orden inverso
  • Alternativa $QUERY: Recorre todos los nodos en todos los niveles en una sola llamada (devuelve la referencia completa de subíndices)

Notas Detalladas

Descripción general

Los globals del mundo real típicamente tienen múltiples niveles de subíndices. Para recorrer todos los datos, se anidan bucles $ORDER, uno por cada nivel. Cada bucle itera independientemente su nivel de subíndice dentro del contexto establecido por los bucles externos.

Recorrido de dos niveles

 // Build a two-level structure
 SET ^students("Math", "Alice") = 95
 SET ^students("Math", "Bob") = 87
 SET ^students("Science", "Alice") = 92
 SET ^students("Science", "Charlie") = 88

 // Nested traversal
 SET subject = ""
 FOR {
     SET subject = $ORDER(^students(subject))
     QUIT:subject=""
     WRITE "Subject: ", subject, !
     SET student = ""
     FOR {
         SET student = $ORDER(^students(subject, student), 1, grade)
         QUIT:student=""
         WRITE "  ", student, " = ", grade, !
     }
 }
 // Output:
 //   Subject: Math
 //     Alice = 95
 //     Bob = 87
 //   Subject: Science
 //     Alice = 92
 //     Charlie = 88

Recorrido de tres niveles

 // Three-level structure: ^data(year, month, day) = info
 SET ^data(2025, 1, 15) = "Event A"
 SET ^data(2025, 3, 1) = "Event B"
 SET ^data(2025, 3, 20) = "Event C"
 SET ^data(2026, 1, 5) = "Event D"

 SET year = ""
 FOR {
     SET year = $ORDER(^data(year))
     QUIT:year=""
     SET month = ""
     FOR {
         SET month = $ORDER(^data(year, month))
         QUIT:month=""
         SET day = ""
         FOR {
             SET day = $ORDER(^data(year, month, day), 1, info)
             QUIT:day=""
             WRITE year, "-", month, "-", day, ": ", info, !
         }
     }
 }

Recorrido inverso en cualquier nivel

 // Reverse outer level, forward inner level
 SET subject = ""
 FOR {
     SET subject = $ORDER(^students(subject), -1)
     QUIT:subject=""
     WRITE "Subject: ", subject, !
     SET student = ""
     FOR {
         SET student = $ORDER(^students(subject, student), 1, grade)
         QUIT:student=""
         WRITE "  ", student, " = ", grade, !
     }
 }
 // Output: Science first, then Math (reverse alphabetical)

Uso de $QUERY para recorrido plano

$QUERY recorre todos los nodos en todos los niveles en un solo bucle, devolviendo la referencia completa como cadena:

 SET ref = "^students"
 FOR {
     SET ref = $QUERY(@ref)
     QUIT:ref=""
     WRITE ref, " = ", @ref, !
 }
 // Output:
 //   ^students("Math","Alice") = 95
 //   ^students("Math","Bob") = 87
 //   ^students("Science","Alice") = 92
 //   ^students("Science","Charlie") = 88

Referencias de Documentación

4. $DATA para verificar la existencia de nodos

Puntos Clave

  • $DATA devuelve 0: El nodo no existe
  • $DATA devuelve 1: El nodo existe y tiene datos, pero no tiene descendientes
  • $DATA devuelve 10: El nodo tiene descendientes pero no tiene datos en este nivel
  • $DATA devuelve 11: El nodo tiene tanto datos como descendientes
  • Abreviatura $D: $D es una abreviatura válida para $DATA
  • Forma de dos argumentos: $DATA(var, target) almacena el valor del nodo en target si existe

Notas Detalladas

Descripción general

$DATA (abreviado $D) es la forma estándar de verificar si una variable o nodo global existe antes de acceder a él. Devuelve un entero que indica tanto si existen datos en ese nodo como si existen nodos descendientes debajo de él.

Valores de retorno explicados

 KILL myarray
 SET myarray(1) = "data"
 SET myarray(2, "child") = "nested"
 SET myarray(3) = "parent"
 SET myarray(3, "child") = "nested"

 WRITE $DATA(myarray(0)), !     // 0  - does not exist
 WRITE $DATA(myarray(1)), !     // 1  - has data, no children
 WRITE $DATA(myarray(2)), !     // 10 - no data, has children
 WRITE $DATA(myarray(3)), !     // 11 - has data AND children

Uso de $DATA en lógica condicional

 // Check if a node exists before reading
 IF $DATA(^config("timeout")) {
     SET timeout = ^config("timeout")
 } ELSE {
     SET timeout = 30  // default
 }

 // More concise: check for data presence (1 or 11)
 // Use modular arithmetic: $DATA returns 1 or 11 when data exists
 IF $DATA(^config("timeout")) # 2 {
     // Node has data (return value is 1 or 11)
     SET timeout = ^config("timeout")
 }

 // Check for descendants (10 or 11)
 IF $DATA(^config("timeout")) \ 10 {
     // Node has descendant subscripts
     WRITE "Has child nodes", !
 }

Forma de dos argumentos

El segundo argumento recibe el valor del nodo (si existen datos), evitando una lectura separada:

 IF $DATA(^config("timeout"), value) # 2 {
     WRITE "Timeout is: ", value, !
 } ELSE {
     WRITE "No timeout configured", !
 }

Patrón común: Verificación de existencia de variables

 // Check if a local variable is defined
 IF '$DATA(myVar) {
     SET myVar = "default"
 }

 // Shorthand $D
 IF $D(^globalNode) WRITE "Exists", !

 // $DATA on undefined variables returns 0
 KILL x
 WRITE $D(x), !    // 0
 SET x = 5
 WRITE $D(x), !    // 1
 SET x(1) = 10
 WRITE $D(x), !    // 11

$DATA vs $GET

$DATA le dice SI algo existe; $GET recupera el valor con un valor predeterminado. Sirven para propósitos diferentes:

 // $GET returns the value or a default
 SET timeout = $GET(^config("timeout"), 30)

 // $DATA tells you about existence and structure
 SET status = $DATA(^config("timeout"))

Referencias de Documentación

Resumen de Preparación para el Examen

Conceptos críticos a dominar:

  1. Colación de subíndices: Los numéricos canónicos se ordenan primero por valor, luego las cadenas se ordenan por orden de bytes ASCII
  2. Forma canónica: Saber qué valores son canónicos (1, -2, 3.14) vs no canónicos ("01", "3.0", "+5")
  3. Patrón $ORDER: Inicio con cadena vacía, bucle FOR, terminación QUIT:key=""
  4. Dirección de $ORDER: 1 para adelante (predeterminado), -1 para reverso
  5. Tercer argumento de $ORDER: Recupera datos por referencia, más eficiente para globals
  6. $ORDER anidado: Un bucle por nivel de subíndice para recorrido multinivel
  7. Valores de retorno de $DATA: 0 (nada), 1 (solo datos), 10 (solo hijos), 11 (ambos)
  8. Aritmética modular de $DATA: `# 2` para verificar datos, `\ 10` para verificar descendientes

Escenarios comunes de examen:

  • Predecir el orden de subíndices en un array mixto numérico/cadena
  • Escribir un bucle $ORDER correcto para recorrer un global
  • Determinar la salida de un recorrido $ORDER anidado
  • Elegir entre $DATA, $GET y $ORDER para una tarea dada
  • Identificar valores de subíndices canónicos vs no canónicos
  • Usar $ORDER con dirección -1 para recorrido inverso
  • Usar $QUERY vs $ORDER anidado para recorrido plano

Recomendaciones de práctica:

  • Crear arrays con subíndices mixtos numéricos y de cadena y verificar el orden de recorrido
  • Escribir bucles $ORDER anidados para globals de dos y tres niveles
  • Experimentar con $DATA en nodos con y sin datos y descendientes
  • Usar $ORDER con el tercer argumento y comparar el rendimiento con lecturas separadas
  • Practicar recorrido inverso y recorrido desde un punto de inicio específico
  • Probar $QUERY en globals multinivel y comparar con el enfoque de $ORDER anidado

Report an Issue