Le concept Pairs and Dicts a présenté les bases du type dictionnaire de Julia.
Comme les Dict sont un type de collection important et très utilisé, il existe aussi de nombreuses fonctions pour les manipuler.
La façon la plus simple de récupérer une entrée est de passer par sa clé (comme dans beaucoup de langages).
Cependant, demander une clé qui n'existe pas déclenche une 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
On pourrait vérifier la présence de la clé avant d'essayer de la récupérer (avec quelque chose comme haskey(d, 4)), mais il peut être utile d'obtenir directement une valeur par défaut pour les clés absentes.
Pour cela, on dispose de la fonction get() et de sa variante mutante get!().
La différence entre les deux tient au fait d'ajouter ou non la clé « manquante » au 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'
On a vu dans le concept précédent que delete!() supprime une entrée par sa clé.
On peut combiner cela avec la récupération de la valeur, grâce à la fonction 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'
Quand on itère sur un Dict, on obtient par défaut des paires key => value.
On accède aux composants avec la nomenclature habituelle .first et .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')
Souvent, on veut utiliser seulement les clés ou seulement les valeurs, ce qui est facile.
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)
Combiner deux Dict peut être utile, mais il faut être clair sur ce qu'on veut faire des clés présentes dans plusieurs dictionnaires en entrée.
Les fonctions merge() et merge!() combinent un nombre arbitraire de Dict fournis, de gauche à droite.
Les valeurs des clés existantes sont simplement écrasées, donc c'est le Dict le plus à droite qui « gagne ».
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)
Les fonctions mergewith() et mergewith!() permettent de fournir une fonction en premier argument, pour définir comment fusionner les clés répétées.
L'addition en est un exemple simple.
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"
À noter que les fonctions mergewith sont un bon exemple de fonctions d'ordre supérieur en action.
Mecha Munch™, une entreprise d'automatisation des courses alimentaires, vient de t'embaucher pour travailler sur son application de commande. Ton équipe a pour mission de construire un MVP (produit minimum viable) qui gère toutes les activités de base du panier d'achat, ce qui permet aux utilisateurs d'ajouter, de supprimer et de trier leurs commandes de courses. Heureusement, une autre équipe s'occupe de tout ce qui touche à l'argent et au paiement !
Le MVP doit permettre à l'utilisateur d'ajouter des articles à son panier d'achat. Il peut s'agir d'un seul article ou de plusieurs articles à la fois. Comme il s'agit d'un MVP, la quantité d'un article est indiquée par des répétitions. Si un utilisateur veut ajouter 2 Oranges, « Oranges » apparaîtra deux fois dans l'itérable d'entrée. Si l'utilisateur a déjà cet article dans son panier, la quantité correspondante du panier doit être augmentée de 1. Si l'article est nouveau dans le panier, il doit y être ajouté avec une quantité de 1.
Crée la fonction add_items!(<cart>, <items>) qui prend en arguments un dictionnaire représentant le panier et un itérable d'articles à ajouter.
Elle doit renvoyer le dictionnaire du panier d'achat mis à jour pour l'utilisateur.
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
L'application dispose d'une section « idées » remplie de recettes toutes prêtes issues de diverses cuisines. L'utilisateur peut sélectionner l'une de ces recettes et faire ajouter automatiquement tous ses ingrédients à son panier d'achat. Le chef de projet t'a demandé de créer un moyen de modifier ces recettes « idées », car l'équipe de contenu ne cesse de changer les ingrédients et les quantités.
Crée la fonction update_recipes!(<ideas>, <updates>) qui prend en arguments un dictionnaire « idées » et un dictionnaire de mises à jour de recettes.
La fonction doit renvoyer le dictionnaire « idées » mis à jour.
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)
L'application doit envoyer le panier d'un utilisateur donné au magasin pour qu'il soit préparé. Cependant, les préparateurs du magasin ont besoin de savoir où trouver chaque article grâce à un code de rayon unique. Le « panier de préparation » doit être trié selon les codes de rayon des articles, afin d'aider les préparateurs à être plus efficaces.
Crée la fonction send_to_store(<cart>, <aislecodes>) qui prend un panier d'achat d'utilisateur et un dictionnaire contenant les numéros de rayon du magasin.
La fonction doit renvoyer un « panier de préparation » combiné, contenant une paire aislecode => quantity pour chaque article commandé par le client.
Les articles doivent apparaître dans l'ordre de leurs codes de rayon.
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
L'application ne peut pas se contenter d'enregistrer sans fin les commandes des clients. Au bout d'un moment, le magasin va finir par être à court de divers produits. Ton MVP doit donc mettre à jour l'inventaire du magasin chaque fois qu'un utilisateur envoie sa commande. Sinon, les clients commanderont des produits qui ne sont en réalité pas disponibles.
Crée la fonction update_store_inventory!(<inventory>, <cart>) qui prend un cart et un inventory.
La fonction doit réduire les quantités de l'inventaire du magasin du nombre « commandé » dans le cart, puis renvoyer un dictionnaire des articles en rupture de stock (c'est-à-dire ceux qui ont atteint 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
Au-delà de la mise à jour de l'inventaire, ton application peut aussi aider à le réapprovisionner. Ton MVP doit pouvoir préparer une mini-commande des articles en rupture de stock après la commande d'un client. Cela permettra de déclencher plus facilement une commande importante. De plus, il arrive que des articles promotionnels à durée limitée ne figurent pas dans la liste de stock. Si ces articles sont épuisés avant la fin de la période promotionnelle, c'est qu'ils ont rencontré du succès et ils peuvent être ajoutés à la liste de stock.
Crée la fonction reorder!(<outofstock>, <stock>) qui prend outofstock et stock.
Le dictionnaire stock contient les articles standards du stock et leurs quantités de réapprovisionnement.
Le dictionnaire outofstock est le résultat de update_store_inventory!, à savoir un dictionnaire des articles dont la quantité est nulle.
Les quantités de outofstock doivent être mises à jour pour refléter celles de stock, puis renvoyées.
De plus, les articles présents dans outofstock mais absents de stock doivent être ajoutés à stock avec une quantité par défaut 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
Inscris-toi sur Exercism pour apprendre et maîtriser Julia avec 35 concepts128 exercices, et un vrai mentorat humain, le tout gratuitement.