El concepto Pares y diccionarios trataba los fundamentos del tipo diccionario de Julia.
Como los Dict son un tipo de colección importante y muy usado, también existen muchas funciones para manipularlos.
La forma más sencilla de recuperar una entrada es por su clave (como en muchos lenguajes).
Sin embargo, pedir una clave que no existe provoca un KeyError.
julia> d = Dict(i => ('a':'c')[i] for i in 1:3)
Dict{Int64, Char} with 3 entries:
2 => 'b'
3 => 'c'
1 => 'a'
julia> d[2]
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
# missing key
julia> d[4]
ERROR: KeyError: key 4 not found
Podríamos comprobar si la clave existe antes de intentar recuperarla (con algo como haskey(d, 4)), pero puede resultar útil obtener directamente un valor por defecto para las claves que faltan.
Para esto tenemos la función get() y su variante mutadora get!().
Se diferencian en si una clave «que falta» se añade al Dict.
julia> get(d, 4, '!')
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
# get() leaves Dict unchanged
julia> d
Dict{Int64, Char} with 3 entries:
2 => 'b'
3 => 'c'
1 => 'a'
julia> get!(d, 4, '!')
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
# get!() adds new key to Dict
julia> d
Dict{Int64, Char} with 4 entries:
4 => '!'
2 => 'b'
3 => 'c'
1 => 'a'
Vimos en el concepto anterior que delete!() elimina una entrada por su clave.
Esto se puede combinar con la obtención del valor usando la función pop!().
julia> d
Dict{Int64, Char} with 4 entries:
4 => '!'
2 => 'b'
3 => 'c'
1 => 'a'
# popped value is returned
julia> pop!(d, 3)
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
# Dict is shrunk
julia> d
Dict{Int64, Char} with 3 entries:
4 => '!'
2 => 'b'
1 => 'a'
Iterar sobre un Dict produce por defecto pares key => value.
Se puede acceder a sus componentes con la nomenclatura habitual .first y .second.
# d is from the previous example
julia> [(kv.first, kv.second) for kv in d]
3-element Vector{Tuple{Int64, Char}}:
(4, '!')
(2, 'b')
(1, 'a')
A menudo queremos usar solo las claves o solo los valores, lo cual es fácil.
julia> [k for k in keys(d)]
3-element Vector{Int64}:
4
2
1
julia> [v for v in values(d)]
3-element Vector{Char}:
'!': ASCII/Unicode U+0021 (category Po: Punctuation, other)
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
Combinar dos Dict puede ser útil, pero debemos tener claro qué queremos hacer con las claves que están presentes en más de uno de los diccionarios de entrada.
Las funciones merge() y merge!() combinan un número arbitrario de Dict proporcionados, de izquierda a derecha.
Los valores de las claves existentes simplemente se sobrescriben, de modo que el Dict situado más a la derecha «gana».
julia> d3 = Dict("color" => "red", "shape" => "circle")
Dict{String, String} with 2 entries:
"shape" => "circle"
"color" => "red"
julia> d4 = Dict("color" => "blue", "coords" => (3, 2))
Dict{String, Any} with 2 entries:
"color" => "blue"
"coords" => (3, 2)
# color from d3 is lost
julia> merge(d3, d4)
Dict{String, Any} with 3 entries:
"shape" => "circle"
"color" => "blue"
"coords" => (3, 2)
Las funciones mergewith() y mergewith!() nos permiten pasar una función como primer argumento para definir cómo se deben combinar las claves repetidas.
La suma es un ejemplo sencillo.
julia> d5 = Dict("color" => "red", "area" => 3)
Dict{String, Any} with 2 entries:
"area" => 3
"color" => "red"
julia> d6 = Dict("shape" => "circle", "area" => 5)
Dict{String, Any} with 2 entries:
"shape" => "circle"
"area" => 5
# area values are added, not overwritten
julia> mergewith(+, d5, d6)
Dict{String, Any} with 3 entries:
"shape" => "circle"
"area" => 8
"color" => "red"
Ten en cuenta que las funciones mergewith son un buen ejemplo de funciones de orden superior en acción.
Mecha Munch™, una empresa de automatización de la compra, acaba de contratarte para que trabajes en su aplicación de pedidos. Tu equipo tiene la tarea de crear un MVP (producto mínimo viable) que gestione todas las actividades básicas del carrito de la compra, permitiendo a los usuarios añadir, eliminar y ordenar sus pedidos. Por suerte, ¡otro equipo se encarga de todo lo relacionado con el dinero y el pago!
El MVP debe permitir al usuario añadir artículos a su carrito de la compra. Puede ser un solo artículo o varios a la vez. Como se trata de un MVP, la cantidad de cada artículo se indica mediante repeticiones. Si un usuario quiere añadir 2 naranjas, «Oranges» aparecerá dos veces en el iterable de entrada. Si el usuario ya tiene el artículo en su carrito, la cantidad del carrito debe aumentarse en 1. Si el artículo es nuevo en el carrito, debe añadirse con una cantidad de 1.
Crea la función add_items!(<cart>, <items>), que recibe como argumentos un diccionario del carrito y un iterable de artículos que añadir.
Debe devolver el diccionario actualizado del carrito de la compra del usuario.
julia> additems!(Dict("Banana" => 3, "Apple" => 2, "Orange" => 1),
("Apple", "Apple", "Orange", "Apple", "Banana"))
Dict{String, Int64} with 3 entries:
"Apple" => 5
"Orange" => 2
"Banana" => 4
julia> additems!(Dict("Banana" => 3, "Apple" => 2, "Orange" => 1),
["Banana", "Orange", "Blueberries", "Banana"])
Dict{String, Int64} with 4 entries:
"Blueberries" => 1
"Apple" => 2
"Orange" => 2
"Banana" => 5
La aplicación tiene una sección de «ideas» que se llena con recetas terminadas de distintas cocinas. El usuario puede seleccionar cualquiera de estas recetas y hacer que todos sus ingredientes se añadan automáticamente a su carrito de la compra. El jefe de proyecto te ha pedido que crees una forma de editar estas recetas de «ideas», ya que el equipo de contenidos no para de cambiar ingredientes y cantidades.
Crea la función update_recipes!(<ideas>, <updates>), que recibe como argumentos un diccionario de «ideas» y un diccionario de actualizaciones de recetas.
La función debe devolver el diccionario de «ideas» actualizado.
julia>update_recipes!(
Dict("Banana Bread" => Dict("Banana" => 1, "Apple" => 1, "Walnuts" => 1, "Flour" => 1, "Eggs" => 2, "Butter" => 1),
"Raspberry Pie" => Dict("Raspberry" => 1, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1)),
Dict("Banana Bread" => Dict("Banana" => 4, "Walnuts" => 2, "Flour" => 1, "Butter" => 1, "Milk" => 2, "Eggs" => 3))
)
Dict{String, Dict{String, Int64}} with 2 entries:
"Raspberry Pie" => Dict("Cream Custard"=>1, "Raspberry"=>1, "Orange"=>1, "Pie Crust"=>1)
"Banana Bread" => Dict("Eggs"=>3, "Flour"=>1, "Milk"=>2, "Butter"=>1, "Walnuts"=>2, "Banana"=>4)
julia> update_recipes!(
Dict("Banana Bread" => Dict("Banana" => 1, "Apple" => 1, "Walnuts" => 1, "Flour" => 1, "Eggs" => 2, "Butter" => 1),
"Raspberry Pie" => Dict("Raspberry" => 1, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1),
"Pasta Primavera" => Dict("Eggs" => 1, "Carrots" => 1, "Spinach" => 2, "Tomatoes" => 3, "Parmesan" => 2, "Milk" => 1, "Onion" => 1)),
Dict("Raspberry Pie" => Dict("Raspberry" => 3, "Orange" => 1, "Pie Crust" => 1, "Cream Custard" => 1, "Whipped Cream" => 2),
"Pasta Primavera" => Dict("Eggs" => 1, "Mixed Veggies" => 2, "Parmesan" => 2, "Milk" => 1, "Spinach" => 1, "Bread Crumbs" => 1),
"Blueberry Crumble" => Dict("Blueberries" => 2, "Whipped Creme" => 2, "Granola Topping" => 2, "Yogurt" => 3))
)
Dict{String, Dict{String, Int64}} with 4 entries:
"Raspberry Pie" => Dict("Raspberry"=>3, "Orange"=>1, "Pie Crust"=>1, "Cream Custard"=>1, "Whipped Cream"=>2)
"Pasta Primavera" => Dict("Eggs"=>1, "Mixed Veggies"=>2, "Parmesan"=>2, "Milk"=>1, "Spinach"=>1, "Bread Crumbs"=>1)
"Blueberry Crumble" => Dict("Blueberries"=>2, "Whipped Creme"=>2, "Granola Topping"=>2, "Yogurt"=>3)
"Banana Bread" => Dict("Banana"=>1, "Apple"=>1, "Walnuts"=>1, "Flour"=>1, "Eggs"=>2, "Butter"=>1)
La aplicación necesita enviar el carrito de un usuario determinado a la tienda para su preparación. Sin embargo, los empleados de la tienda necesitan saber dónde se puede encontrar cada artículo mediante un código de pasillo único. El «carrito de preparación» debe ordenarse según los códigos de pasillo de los artículos para ayudar a los empleados a ser más eficientes.
Crea la función send_to_store(<cart>, <aislecodes>), que recibe un carrito de la compra del usuario y un diccionario con los números de pasillo de la tienda.
La función debe devolver un «carrito de preparación» combinado que tenga un par aislecode => quantity por cada artículo que pide el cliente.
Los artículos deben aparecer en el orden de sus códigos de pasillo.
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2, "Chocolate" => 2);
julia> aislecodes = Dict("Orange" => 2.4, "Milk" => 3.3, "Banana" => 2.6, "Apple" => 2.2, "Chocolate" => 5.7);
julia> send_to_store(cart, aislecodes)
5-element Vector{Pair{Float64, Int64}}:
2.2 => 2
2.4 => 1
2.6 => 3
3.3 => 2
5.7 => 2
La aplicación no puede limitarse a hacer pedidos de clientes sin fin. Con el tiempo, la tienda se quedará sin varios productos. Así que tu MVP debe actualizar el inventario de la tienda cada vez que un usuario envía su pedido a la tienda. De lo contrario, los clientes pedirán productos que en realidad no están disponibles.
Crea la función update_store_inventory!(<inventory>, <cart>), que recibe un cart y un inventory.
La función debe reducir las cantidades del inventario de la tienda según el número «ordered» del «cart» y, a continuación, devolver un diccionario con los artículos que se hayan agotado (es decir, los que hayan llegado a 0).
julia> inventory = Dict("Banana" => 15, "Apple" => 12, "Orange" => 1, "Milk" => 4);
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2);
julia> update_store_inventory!(inventory, cart)
Dict{String, Int64} with 1 entry:
"Orange" => 0
julia> inventory
Dict{String, Int64} with 4 entries:
"Milk" => 2
"Apple" => 10
"Orange" => 0
"Banana" => 12
Además de actualizar el inventario, tu aplicación puede ayudar a reponerlo. Tu MVP debe ser capaz de preparar un mini pedido con los artículos que se hayan agotado tras el pedido de un cliente. Esto ayudará a que lanzar un pedido grande sea más sencillo. Es más, a veces hay artículos en promoción por tiempo limitado que no están en la lista de existencias. Si se han agotado antes de que termine el periodo de promoción, es que han tenido éxito y pueden añadirse a la lista de existencias.
Crea la función reorder!(<outofstock>, <stock>), que recibe outofstock y stock.
El diccionario stock contiene los artículos estándar en existencias y sus cantidades de reposición.
El diccionario outofstock es el resultado de update_store_inventory!, es decir, un diccionario de artículos con cantidad cero.
Las cantidades de outofstock deben actualizarse para reflejar las de stock y devolverse.
Además, cualquier artículo de outofstock que no esté presente en stock debe añadirse a stock con una cantidad predeterminada de 100.
julia> inventory = Dict("Banana" => 15, "Apple" => 12, "Orange" => 1, "Milk" => 4, "Chocolate" => 2);
julia> cart = Dict("Orange" => 1, "Milk" => 2, "Banana" => 3, "Apple" => 2, "Chocolate" => 2);
julia> stock = Dict("Banana" => 150, "Apple" => 120, "Orange" => 120, "Milk" => 45);
julia> outofstock = update_store_inventory!(inventory, cart)
Dict{String, Int64} with 1 entry:
"Orange" => 0
"Chocolate" => 0
julia> reorder!(outofstock, stock)
Dict{String, Int64} with 1 entry:
"Orange" => 120
"Chocolate" => 100
julia> stock
Dict{String, Int64} with 5 entries:
"Milk" => 45
"Apple" => 120
"Chocolate" => 100
"Orange" => 120
"Banana" => 150
Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.