Hechicería Cotlang: rituales avanzados con $OUTPUT y compañía (parte 3)
Has caminado por los pasillos de la Academia Cotalker. Has aprendido a invocar hechizos básicos como $VALUE, has conjurado fórmulas más avanzadas usando filtros y transformaciones, y has sobrevivido a las enseñanzas del Brujo Calvo. Pero lo que viene ahora... es el verdadero poder de un Archimago.
Este es el nivel donde los hechizos no solo responden, sino que se adaptan, consultan los planos superiores de Cotalker, y abren portales hacia lo imposible. Prepárate, porque hoy dominarás los conjuros más antiguos del grimorio Cotlang.
Eso sí, ten cuidado: este nivel solo debe ser manejado por magos experimentados. Un hechizo de este poder, en manos inexpertas, puede ser devastador. Si aún no conoces los hechizos fundamentales, te dejo las dos clases previas que necesitas:


Capítulo 1: Las múltiples dimensiones de Cotalker
Ya dominas el arte de leer el Contexto, ese objeto mágico que contiene toda la información del formulario (y más). Pero en el nivel avanzado, existe un plano aún más misterioso y poderoso: la dimensión de los Resultados.
Imagina que cada etapa de tu rutina es como un ritual distinto, y Resultados es el historial donde quedan anotados todos los conjuros realizados. Es un objeto JSON igual al Contexto, pero con una pequeña diferencia: aquí viven las respuestas de todas las etapas de la rutina, no del formulario actual.
¿Creaste una etapa que suma dos números? Resultados te mostrará exactamente cuál fue el resultado de ese hechizo. ¿Pasaste por varias etapas? Aquí puedes consultar el eco de cada conjuro lanzado en el pasado.
De forma sencilla, podríamos decir que el Contexto es tu materia prima (los datos que quieres transformar), y los Resultados son el libro donde se anotan los efectos de cada hechizo lanzado.
1.1 Contexto
{
"answer": {
"_id": "abc001c5c77b31459033e88",
"data": [
{
"question": "abc001c5c77b31459033e89",
"contentType": "application/vnd.cotalker.survey+textinput",
"identifier": "applicant_name",
"display": ["Nombre del postulante"],
"responses": ["Hermione Granger"],
"process": ["Hermione Granger"]
},
{
"question": "abc001c5c77b31459033e90",
"contentType": "application/vnd.cotalker.survey+textinput",
"identifier": "applicant_email",
"display": ["Email del postulante"],
"responses": ["[email protected]"],
"process": ["[email protected]"]
},
{
"question": "abc001c5c77b31459033e91",
"contentType": "application/vnd.cotalker.survey+number",
"identifier": "years_experience",
"display": ["Años de experiencia"],
"responses": [4],
"process": [4]
},
{
"question": "abc001c5c77b31459033e92",
"contentType": "application/vnd.cotalker.survey+property",
"identifier": "position_applied",
"display": ["Cargo postulado"],
"responses": [
"{\"_id\":\"6882ea3b5168f579a647cba7\",\"name\":{\"code\":\"senior_dev\",\"display\":\"Desarrollador Senior\"},\"department\":\"Ingeniería\",\"isActive\":true}"
],
"process": ["6882ea3b5168f579a647cba7"]
},
{
"question": "abc001c5c77b31459033e93",
"contentType": "application/vnd.cotalker.survey+property",
"identifier": "technical_skills",
"display": ["Habilidades técnicas"],
"responses": [
"{\"skill\":\"javascript\",\"level\":5}",
"{\"skill\":\"python\",\"level\":4}",
"{\"skill\":\"sql\",\"level\":3}"
],
"process": [
"6883013e0447eee0d4d68e33",
"6883014228073debd112d555",
"6883014689d1e9b3f7dd89b3"
]
},
{
"question": "abc001c5c77b31459033e93",
"contentType": "application/vnd.cotalker.survey+property",
"identifier": "reference",
"display": ["Habilidades técnicas"],
"responses": "[{\"name\":\"Minerva McGonagall\",\"relation\":\"jefe directo\"},{\"name\":\"Severus Snape\",\"relation\":\"compañero\"},{\"name\":\"Sybill Trelawney\",\"relation\":\"jefe directo\"},{\"name\":\"Remus Lupin\",\"relation\":\"mentor\"},{\"name\":\"Alastor Moody\",\"relation\":\"jefe directo\"}]",
"process": [
"6883013e0447eee0d4d68e33",
"6883014228073debd112d555",
"6883014689d1e9b3f7dd89b3"
]
},
{
"question": "688305f002c66d79b21ec8f1",
"contentType": "application/vnd.cotalker.survey+text",
"identifier": "documents_url",
"display": ["Documentos"],
"responses": [
"https://hogwarts.edu/7be5abe7-81cd-46bd-b5d3-391bba33a796"
],
"process": [
"https://hogwarts.edu/7be5abe7-81cd-46bd-b5d3-391bba33a796"
]
},
{
"question": "68830ecac5461d68e11f7932",
"contentType": "application/vnd.cotalker.survey+textinput",
"identifier": "idiomas_hablados",
"display": ["Idiomas Hablados"],
"responses": [
"__ON__",
"__ON__",
"__OFF__",
"__ON__",
"__OFF__"
],
"process": ["ingles", "español", "aleman"]
}
],
"uuid": "abc0014e9d7024fc8624c8a6",
"company": "abc0015ef618ab89d55940aa",
"user": "abc00162f5af107b02ec0991"
}
}
1.2 Resultados
{
"0": {
"key": "is_valid_email",
"name": "FCIfElse",
"status": "IF",
"result": {
"lhs": "ok",
"rhs": "ok",
"res": true
},
"next": "evaluar_experiencia"
},
"1": {
"key": "evaluar_experiencia",
"name": "CCJS",
"status": "SUCCESS",
"result": {
"error": null,
"data": {
"aprobado": true,
"puntaje": 85
}
},
"next": "asignar_entrevistador"
},
"2": {
"key": "patch_asset",
"name": "NWRequest",
"status": "SUCCESS",
"result": {
"statusCode": 200,
"data": {
"_id": "6882e7b3c0714818f5ada280",
"company": "abc0015ef618ab89d55940aa",
"propertyType": "asset_wf",
"name": {
"code": "unique-6882e7ba5d4ce4fd274e7923",
"display": "Entrevista: Hermione Granger"
},
"isActive": true,
"schemaInstance": {
"name": "Hermione Granger",
"email": "[email protected]",
"cargo": "6882e9bc7ca546ea86b3a83d"
}
}
},
"next": "get_documents"
},
"3": {
"key": "get_documents",
"name": "NWRequest",
"status": "SUCCESS",
"result": {
"statusCode": 200,
"data": {
"responses": [
{
"type": "cv",
"file_url": "https://hogwarts.edu/cvs/hermione.pdf"
},
{
"type": "cv",
"file_url": "https://hogwarts.edu/cvs/hermione_eng.pdf"
},
{
"type": "portfolio"
},
{
"type": "recommendation_letter"
}
]
}
}
}
}
Como podrás ver, el objeto de Resultados posee claves numéricas como "0", "1", "2", etc. Al principio, esto puede parecer extraño, pero no te preocupes, no afecta en nada el uso de Cotlang: la clave numérica solo sirve para identificar el resultado dentro del historial.
En la práctica, siempre buscarás los datos usando el key de cada etapa (por ejemplo, "evaluar_experiencia") y no por el número. Así que relájate y sigue conjurando sin miedo: la magia sigue funcionando igual.
Si para acceder a Contexto usábamos la función de cotlang $VALUE, para acceder a Resultados debemos hacer uso de un nuevo hechizo: $OUTPUT.
Como curiosidad, existe una tercera dimensión en este universo: Rutina. Es el plano donde podemos observar en detalle cómo nuestros hechizos transforman los datos del Contexto en Resultados. Una herramienta fundamental para corregir errores y dominar el arte del debug mágico. Pero ese portal lo abriremos en un próximo artículo...
Capítulo 2: Moviéndonos entre dimensiones
"0", "1", "2") en Javascript puede resultar confuso y poco práctico, especialmente cuando quieres encontrar información específica dentro de un objeto grande. Por eso, para simplificar la explicación y el código de los ejemplos, supondremos que cada resultado tiene como clave el nombre de la etapa, por ejemplo evaluar_experiencia en vez de "1".Ejemplo 1: Comenzaremos con un ejemplo simple. Supongamos que queremos acceder al puntaje que se encuentra en la etapa evaluar_experiencia.
- En Javascript:
output.evaluar_experiencia.result.data.puntaje - En Cotlang:
$OUTPUT#evaluar_experiencia#data|puntaje - Resultado:
85
Explicación:
$OUTPUT: "Cotlang, invoca los resultados almacenados de las etapas anteriores".#evaluar_experiencia:"Dirígete a la etapa llamada"evaluar_experiencia"".#data: "Dentro de esa etapa, busca el objeto llamado"data"".|puntaje: "Ahora dime el puntaje que obtuvo el usuario en esa etapa".
Ejemplo 2: En este caso queremos saber el code de la propiedad que se está actualizando (la variable code de patch_asset)
- En Javascript:
output.patch_asset.result.data.name.code - En Cotlang:
$OUTPUT#patch_asset#data|name|code - Resultado:
unique-6882e7ba5d4ce4fd274e7923
Explicación:
$OUTPUT: "Cotlang, invoca los resultados almacenados de las etapas anteriores".#patch_asset: “Abre la etapa llamada"patch_asset"”.|data: “Dentro de esta etapa, entra en el apartado"data"”.|name: “De"data", ve directamente al subapartado"name"”.|code: “Por último, dime el valor de"code"que se encuentra allí”.
Capítulo 3: Aprendiendo nuevos hechizos
Ahora que dominas el arte de invocar Resultados con $OUTPUT, ha llegado el momento de elevar tu magia un nivel más alto: combinar el poder de $OUTPUT (Resultado) con el de $VALUE (Contexto).
Esta unión te permite crear hechizos aún más poderosos, trayendo información tanto del pasado (resultados de etapas previas) como del presente (contexto actual).
3.1 Convertir listas en diccionarios: Necesitamos enviar la lista de habilidades técnicas a un sistema que solo solo acepta objetos key-value, donde la clave sea el nombre de la habilidad (skill) y el valor el nivel (level).
[arrayToObject=>] es una función que permite transformar un arreglo de objetos en una estructura de tipo objeto (diccionario), utilizando el valor de una propiedad específica de cada elemento como clave, y el valor de otra propiedad como valor asociado.- En Javascript:
return value.answer.data
.find(question => question.identifier === "technical_skills")
.responses.map(JSON.parse)
.reduce((acc, curr) => {
acc[curr.skill] = curr.level;
return acc;
}, {});
- En Cotlang:
$VALUE#answer|data|[find=>identifier=technical_skills]|responses|[json=>parse]|[arrayToObject=>skill=level] - Resultado:
{
"javascript": 5,
"python": 4,
"sql": 3
}Explicación:
[arrayToObject=>skill=level]: Genera un diccionario donde cada clave esskilly el valor asociado eslevel.
3.2 Filtrando claves vacías: El cliente tiene una lista con todos los documentos de tipo cv que el usuario ha subido, pero no todos tienen una URL. Queremos filtrar y quedarnos solo con los que la tengan.
[isDefinedFilter=>] es una función que permite filtrar un arreglo de objetos, retornando únicamente aquellos elementos que contienen una clave específica con un valor definido (es decir, que no sea null, undefined, una cadena vacía o cero).- En Javascript:
output.get_documents.result.data.responses.filter(document => document.file_url) - En Cotlang:
$OUTPUT#get_documents#data|responses|[idDefinedFilter=>file_url] - Resultado:
[
{
"type": "cv",
"file_url": "https://hogwarts.edu/cvs/hermione.pdf"
},
{
"type": "cv",
"file_url": "https://hogwarts.edu/cvs/hermione_eng.pdf"
}
]Explicación:
[isDefinedFilter=>file_url]: “De todos los elementos en responses, quédate únicamente con aquellos que tengan la clavefile_urldefinida (es decir, que no esté vacía, nula ni sea cero)”.
3.3 Filtrando valores de un object array distintos a: Ahora el cliente nos solicita no contar las habilidades que tengan un nivel menor o igual a 3.
[notEqualFilter=>] es una función que permite filtrar un arreglo de objetos, retornando únicamente aquellos elementos cuyo valor en una clave específica no coincide con el valor indicado como condición- En Javascript:
return value.answer.data
.find(question => question.identifier === "technical_skills")
.responses.map(JSON.parse)
.filter(technicalSkill => technicalSkill.level > 3);- En Cotlang:
$VALUE#answer|data|[find=>identifier=technical_skills]|responses|[json=>parse]|[notEqualFilter=>level=2]|[notEqualFilter=>level=1]|[notEqualFilter=>level=3] - Resultado:
[
{
"skill": "javascript",
"level": 5
},
{
"skill": "python",
"level": 4
}
]Explicación:
[notEqualFilter=>level=1]: “Filtra y excluye aquellos objetos donde el valor de level sea igual a 1”.[notEqualFilter=>level=2]: “Luego, excluye los que tengan un nivel igual a 2”.[notEqualFilter=>level=3]: “Finalmente, excluye todos los que tengan nivel igual a 3”.
3.4 Quitando valores de un array: En la lista de idiomas, nos solicitaron excluir el idioma español, ya que la vacante es solo nacional.
[simpleNotEqualFilter=>] es una función que permite filtrar arreglos, retornando únicamente aquellos elementos cuyo valor no coincida con el valor indicado como condición- En Javascript:
return value.answer.data
.find(question => question.identifier === "idiomas_hablados")
.process.filter(idioma => idioma !== "español")- En Cotlang:
$VALUE#answer|data|[find=>identifier=idiomas_hablados]|process|[simpleNotEqualFilter=>español] - Resultado:
[ "ingles", "aleman" ]
Explicación:
[simpleNotEqualFilter=>español]: “Filtra el arreglo para excluir todos los elementos cuyo valor sea igual a español.”
3.5 Obteniendo valores exclusivos en un array: Queremos saber cuántas referencias hay de tipo "jefe directo", porque una nueva regla de negocio exige al menos 3.
[simpleEqualFilter=>] es una función que permite filtrar arreglos, retornando únicamente aquellos elementos cuyo valor coincida con el valor indicado como condición- En Javascript:
return JSON.parse(
value.answer.data.find(question => question.identifier === "reference")
.responses
)
.map(reference => reference.relation)
.filter(relation => relation === "jefe directo").length- En Cotlang:
$VALUE#answer|data|[find=>identifier=reference]|responses|[json=>parse]|[map=>relation]|[simpleEqualFilter=>jefe directo]|[size=>*] - Resultado:
3
Explicación:
[map=>relation]: “Extrae la propiedad relation de cada referencia, formando un nuevo arreglo solo con los tipos de relación.”[simpleEqualFilter=>jefe directo]: “Filtra el arreglo y retorna todas las ocurrencias cuyo valor sea exactamente jefe directo. Si se repite, aparecerá tantas veces como exista.”[size=>*]: “Por último, cuenta la cantidad de veces que aparece el valor jefe directo en el arreglo filtrado.”
Capítulo 3: El ascenso del Archimago
Al dominar estos hechizos avanzados, tus rutinas ya no solo responderán, sino que realmente pensarán por sí solas: podrán adaptarse al entorno, transformar información, saltar entre etapas y filtrar datos como si leyeras la mente de tus usuarios.
Pero no te confíes. Aunque ahora conoces todo lo necesario para convertirte en un auténtico Archimago de Cotlang, todavía quedan por descubrir los últimos conjuros: los llamados “hechizos prohibidos”. Hechizos tan poderosos y ocultos, que quizás ni siquiera puedas encontrar en la documentación oficial de Cotalker.
¿Te atreverás a aprenderlos? Para eso, deberás esperar al último capítulo...
Referencias:





Member discussion