{"id":231988,"date":"2023-01-13T20:10:00","date_gmt":"2023-01-13T17:10:00","guid":{"rendered":"https:\/\/wordpress.mediadoma.com\/?p=231988"},"modified":"2022-11-10T07:07:44","modified_gmt":"2022-11-10T04:07:44","slug":"como-crear-un-mapa-bidireccional-en-javascript","status":"publish","type":"post","link":"https:\/\/wordpress.mediadoma.com\/es\/como-crear-un-mapa-bidireccional-en-javascript\/","title":{"rendered":"C\u00f3mo crear un mapa bidireccional en JavaScript"},"content":{"rendered":"\n<p>Un mapa bidireccional es un modelo de la inform\u00e1tica donde los pares clave-valor tienen una relaci\u00f3n biyectiva 1-1 entre las claves y los valores. Esto nos permite no solo consultar por clave y obtener un valor, sino tambi\u00e9n consultar por valor y obtener la clave. Veamos c\u00f3mo implementar un mapa bidireccional en JavaScript y luego hag\u00e1moslo seguro en TypeScript<\/p>\n<h2>La inform\u00e1tica y las matem\u00e1ticas detr\u00e1s<\/h2>\n<p>Tomemos una definici\u00f3n b\u00e1sica de un mapa bidireccional:<\/p>\n<blockquote>\n<p>En inform\u00e1tica, un <strong>mapa bidireccional<\/strong> es una estructura de datos asociativa en la que los pares clave-valor forman una correspondencia uno a uno. Por lo tanto, la relaci\u00f3n binaria es funcional en cada direcci\u00f3n: cada valor tambi\u00e9n se puede asignar a una clave \u00fanica.<\/p>\n<\/blockquote>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-157501-61e6b9e14822e.png\" data-rel=\"lightbox\" ><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-157501-61e6b9e14822e.png\" alt=\"C\u00f3mo crear un mapa bidireccional en JavaScript\" ><\/a><\/p>\n<p>Cr\u00e9dito: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bidirectional_map\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">Mapa bidireccional<\/a><\/p>\n<p>El mapa bidireccional de la inform\u00e1tica tiene sus ra\u00edces en una funci\u00f3n matem\u00e1tica llamada biyecci\u00f3n. La relaci\u00f3n entre los componentes de un par con cada uno de sus componentes en diferentes conjuntos es una funci\u00f3n biyectiva, tambi\u00e9n llamada funci\u00f3n invertible, que es una funci\u00f3n que funciona en ambos sentidos, emparejando exactamente un elemento de un conjunto con exactamente un elemento de el otro conjunto:<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-157501-61e6b9e2a629e.png\" data-rel=\"lightbox\"><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-157501-61e6b9e2a629e.png\" alt=\"C\u00f3mo crear un mapa bidireccional en JavaScript\" ><\/a><\/p>\n<p>Cr\u00e9dito: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bijection\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">biyecci\u00f3n<\/a><\/p>\n<p>Con esto en mente, podemos saber que una funci\u00f3n biyectiva para los conjuntos anteriores producir\u00e1 algo como:<\/p>\n<p>f (1) = re<br \/>\nf (C) = 3<\/p>\n<p>Otra cosa que surge de la funci\u00f3n biyectiva es que los conjuntos tendr\u00e1n exactamente la misma longitud, de lo contrario la biyecci\u00f3n fallar\u00eda.<\/p>\n<h2>Inicializar el mapa bidireccional<\/h2>\n<p>Modelaremos esto con una clase de <a href=\"https:\/\/startfunction.com\/category\/javascript\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">JavaScript<\/a> que se inicializar\u00e1 con un objeto de pares clave-valor:<\/p>\n<pre><code>const bimap = new BidirectionalMap({\n  a: 'A',\n  b: 'B',\n  c: 'C',\n})<\/code><\/pre>\n<p>Internamente, crearemos dos listas. Una lista almacenar\u00e1 la lista de pares de lo que llamaremos un mapa directo, donde la clave se asigna al valor y ser\u00e1 una copia del objeto que usamos para inicializar el mapa bidireccional. La segunda lista ser\u00e1 lo que llamaremos un mapa inverso, y almacenar\u00e1 una versi\u00f3n del objeto utilizado para inicializar el mapa bidireccional donde los pares clave-valor se han invertido y el &quot;valor&quot; ahora se asigna a la &quot;clave&quot;:<\/p>\n<pre><code>class BidirectionalMap {\n  fwdMap = {}\n  revMap = {}\n\n  constructor(map) {\n      this.fwdMap = { ...map }\n      this.revMap = Object.keys(map).reduce(\n          (acc, cur) =&gt; ({\n              ...acc,\n              [map[cur]]: cur,\n          }),\n          {}) }\n}<\/code><\/pre>\n<p>Tenga en cuenta que, debido a la naturaleza del objeto que inicializa el mapa bidireccional, no puede usar un n\u00famero para la clave, pero a\u00fan puede usarlo como un valor y luego podr\u00e1 consultarlo:<\/p>\n<pre><code>const bimap = new BidirectionalMap({\n  a: 42,\n  b: 'B',\n  c: 'C',\n})<\/code><\/pre>\n<p>Se podr\u00eda escribir una versi\u00f3n m\u00e1s robusta aunque tambi\u00e9n m\u00e1s compleja con el <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">tipo de datos Map<\/a> de JavaScript y permitir claves que sean n\u00fameros, funciones o incluso NaN.<\/p>\n<h2>Obtener un elemento de un mapa bidireccional<\/h2>\n<p>Hasta este punto, tenemos una estructura de datos que alberga dos objetos, uno de ellos es un espejo del otro en t\u00e9rminos de valores clave. Ahora necesitamos un m\u00e9todo para sacar algo de eso. Implementemos una <code>get()<\/code>funci\u00f3n:<\/p>\n<pre><code>  get( key) {\n      return this.fwdMap[key] || this.revMap[key]\n  }<\/code><\/pre>\n<p>Es muy sencillo: si existe en el mapa directo, lo devolvemos, de lo contrario, lo devolvemos desde el mapa inverso. Si no existe, <code>undefined<\/code>ser\u00e1 devuelto.<\/p>\n<p>Ahora podemos obtener algunos elementos como:<\/p>\n<pre><code>console.log( bimap.get('a')) console.log( bimap.get('A')) <\/code><\/pre>\n<h2>Agregar un elemento a un mapa bidireccional<\/h2>\n<p>Nuestro mapa actualmente no tiene una forma de agregar m\u00e1s elementos, as\u00ed que agreguemos una funci\u00f3n para agregar nuevos elementos al mapa:<\/p>\n<pre><code>  add( pair) {\n    this.fwdMap[pair[0]] = pair[1]\n    this.revMap[pair[1]] = pair[0]\n  }<\/code><\/pre>\n<p>Esto recibir\u00e1 una matriz de dos elementos, que luego escribiremos como una tupla con <a href=\"https:\/\/startfunction.com\/tag\/typescript\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">TypeScript<\/a>, y los asignaremos como valores clave en ambas direcciones a los objetos correspondientes.<\/p>\n<p>Y ahora podemos agregar un par y consultar este nuevo par<\/p>\n<pre><code>bimap.add(['d', 'D'])\nconsole.log( bimap.get('D')) <\/code><\/pre>\n<h2>Un mapa bidireccional seguro de tipos en TypeScript<\/h2>\n<p>Para mejorar las cosas y escribir con seguridad, podemos reescribir esto en TypeScript y agregar conceptos de escritura como un objeto gen\u00e9rico al inicializar el mapa y una <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/basic-types.html#tuple\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">tupla<\/a> al agregar un nuevo elemento.<\/p>\n<pre><code>class BidirectionalMap {\n  fwdMap = {}\n  revMap = {}\n\n  constructor(map: { [key: string]: string }) {\n      this.fwdMap = { ...map }\n      this.revMap = Object.keys(map).reduce(\n          (acc, cur) =&gt; ({\n              ...acc,\n              [map[cur]]: cur,\n          }),\n          {}) }\n\n  get(key: string): string | undefined {\n      return this.fwdMap[key] || this.revMap[key]\n  }\n\n  add(pair: [string, string]) {\n    this.fwdMap[pair[0]] = pair[1]\n    this.revMap[pair[1]] = pair[0]\n  }\n}<\/code><\/pre>\n<p>Esta tipificaci\u00f3n ahora hace que nuestro mapa sea perfectamente seguro y garantiza, en este caso, que siempre usaremos cadenas tanto para las claves como para los valores.<\/p>\n<p><div id=\"PostUnique_PostSource\" style=\"padding-top: 50px\">Fuente de grabaci\u00f3n:  <a target=\"_blank\" rel=\"noopener nofollow\" href=\"\/\/startfunction.com\" class=\"external external_icon\">startfunction.com<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Puede consultar estos mapas por clave y obtener su valor o por valor y obtener su clave<\/p>\n","protected":false},"author":1,"featured_media":224548,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_wp_rev_ctl_limit":""},"categories":[892,716,747,727,840],"tags":[1172],"class_list":["post-231988","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-codigo","category-desarrollador","category-fuente-abierta","category-javascript-2","category-tutoriales","tag-affiai-es"],"_links":{"self":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts\/231988","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/comments?post=231988"}],"version-history":[{"count":0,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts\/231988\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/media\/224548"}],"wp:attachment":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/media?parent=231988"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/categories?post=231988"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/tags?post=231988"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}