Rutas
/
Elixir
Elixir
/
Ejercicios
/
Analizador de fechas
Analizador de fechas

Analizador de fechas

Ejercicio de aprendizaje

Introducción

Expresiones regulares

Las expresiones regulares (regex) son una herramienta poderosa para trabajar con strings en Elixir. Las expresiones regulares en Elixir siguen la especificación PCRE (Perl Compatible Regular Expressions). Los patrones de string que representan el significado de la expresión regular se compilan primero y luego se usan para coincidir con todo o parte de un string.

En Elixir, la forma más común de crear expresiones regulares es usando el sigilo ~r. Los sigilos proporcionan atajos de azúcar sintáctico para tareas comunes en Elixir. Para coincidir con un string literal, podemos usar el propio string como patrón después del sigilo.

~r/test/

El operador =~/2 es útil para realizar una coincidencia de regex en un string y devolver un resultado boolean.

"this is a test" =~ ~r/test/
# => true

Dos notas sobre el uso de sigilos:

  • se pueden usar muchos delimitadores diferentes según tus necesidades en lugar de /
  • los patrones de string ya están escapados; cuando escribas el patrón como un string sin usar una regex, tendrás que escapar las barras invertidas (\)

Clases de caracteres

Coincidir con un rango de caracteres usando corchetes [] define una clase de caracteres. Esta coincidirá con cualquier carácter individual de los caracteres de la clase. También puedes especificar un rango de caracteres como a-z, siempre que el inicio y el final representen un rango contiguo de puntos de código.

regex = ~r/[a-z][ADKZ][0-9][!?]/
"jZ5!" =~ regex
# => true
"jB5?" =~ regex
# => false

Las clases de caracteres abreviadas hacen que el patrón sea más conciso. Por ejemplo:

  • \d es la abreviatura de [0-9] (cualquier dígito)
  • \w es la abreviatura de [A-Za-z0-9_] (cualquier carácter de palabra)
  • \s es la abreviatura de [ \t\r\n\f] (cualquier carácter de espacio en blanco)

Cuando se usa una clase de caracteres abreviada fuera de un sigilo, debe escaparse: "\\d"

Alternancias

Las alternancias usan | como carácter especial para indicar la coincidencia con uno u otro

regex = ~r/cat|bat/
"bat" =~ regex
# => true
"cat" =~ regex
# => true

Cuantificadores

Los cuantificadores permiten un patrón repetitivo en la regex. Afectan al grupo que precede al cuantificador.

  • {N, M} donde N es el número mínimo de repeticiones y M es el máximo
  • {N,} coincide con N o más repeticiones
    • {0,} también puede escribirse como *: coincide con cero o más repeticiones
    • {1,} también puede escribirse como +: coincide con una o más repeticiones
  • {,N} coincide con hasta N repeticiones

Grupos

Los paréntesis () se usan para denotar grupos y capturas. El grupo también puede ser capturado en algunos casos para ser devuelto y usado. En Elixir, estos pueden tener nombre o no. Las capturas se nombran añadiendo ?<name> después del paréntesis de apertura. Los grupos funcionan como una sola unidad, como cuando van seguidos de cuantificadores.

regex = ~r/(h)at/
Regex.replace(regex, "hat", "\\1op")
# => "hop"

regex = ~r/(?<letter_b>b)/
Regex.scan(regex, "blueberry", capture: :all_names)
# => [["b"], ["b"]]

Anclas

Las anclas se usan para vincular la expresión regular al principio o al final del string que se va a comparar:

  • ^ ancla al principio del string
  • $ ancla al final del string

Interpolación

Dado que ~r es un atajo para "pattern" |> Regex.escape() |> Regex.compile!(), también puedes usar la interpolación de strings para construir dinámicamente un patrón de expresión regular:

anchor = "$"
regex = ~r/end of the line#{anchor}/
"end of the line?" =~ regex
# => false
"end of the line" =~ regex
# => true

Instrucciones

Te han encargado escribir un servicio que ingiere eventos. Cada evento tiene una fecha asociada, pero te das cuenta de que se están enviando 3 formatos diferentes al endpoint de tu servicio:

  • "01/01/1970"
  • "January 1, 1970"
  • "Thursday, January 1, 1970"

Puedes ver que hay algunas similitudes entre cada uno de ellos y decides escribir algunos patrones de expresiones regulares componibles.

1. Haz coincidir el día, el mes y el año de una fecha

Implementa day/0, month/0 y year/0 para que devuelvan un patrón de string que, al compilarse, coincida con los componentes numéricos de "01/01/1970" (dd/mm/yyyy). El día y el mes pueden aparecer como 1 o 01 (con ceros a la izquierda).

"31" =~ DateParser.day() |> Regex.compile!()
# => true
"12" =~ DateParser.month() |> Regex.compile!()
# => true
"1970" =~ DateParser.year() |> Regex.compile!()
# => true

2. Haz coincidir el día de la semana y el mes del año

Implementa day_names/0 y month_names/0 para que devuelvan un patrón de string que, al compilarse, coincida con el nombre del día de la semana y con el nombre del mes del año, respectivamente.

"Tuesday" =~ DateParser.day_names() |> Regex.compile!()
# => true
"June" =~ DateParser.month_names() |> Regex.compile!()
# => true

3. Captura el día, el mes y el año

Implementa capture_day/0, capture_month/0, capture_year/0, capture_day_name/0 y capture_month_name/0 para que devuelvan un patrón de string que capture los componentes respectivos en los nombres: "day", "month", "year", "day_name", "month_name"

DateParser.capture_month_name()
|> Regex.compile!()
|> Regex.named_captures("December")

# => %{"month_name" => "December"}

4. Combina las capturas para capturar la fecha completa

Implementa capture_numeric_date/0, capture_month_name_date() y capture_day_month_name_date/0 para que devuelvan un patrón de string que capture los componentes de la parte 3 usando el formato de fecha correspondiente:

  • fecha numérica - "01/01/1970"
  • fecha con nombre de mes - "January 1, 1970"
  • fecha con nombre de día y mes - "Thursday, January 1, 1970"
DateParser.capture_numeric_date()
|> Regex.compile!()
|> Regex.named_captures("01/01/1970")

# => %{"day" => "01", "month" => "01", "year" => "1970"}

5. Restringe la captura para que coincida solo con la fecha

Implementa match_numeric_date/0, match_month_name_date/0 y match_day_month_name_date/0 para que devuelvan una expresión regular compilada que solo coincida con la fecha y que además pueda capturar los componentes.

"Thursday, January 1, 1970 was the Unix epoch." =~ DateParser.match_day_month_name_date()
# => false
"Thursday, January 1, 1970" =~ DateParser.match_day_month_name_date()
# => true

DateParser.match_day_month_name_date()
|> Regex.named_captures("Thursday, January 1, 1970")

# => %{
#     "day" => "1",
#     "day_name" => "Thursday",
#     "month_name" => "January",
#     "year" => "1970"
#   }
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Elixir Exercism

¿Listo para empezar Analizador de fechas?

Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.