Let dictionaries respect insertion order
This commit is contained in:
parent
6dcb65e3a3
commit
12be8fe070
@ -674,7 +674,9 @@ Return a new array with the same items, but sorted.
|
||||
A map from string keys to values.
|
||||
|
||||
You can construct a dictionary by enclosing comma-separated `key: value` pairs
|
||||
in parentheses. The values do not have to be of the same type.
|
||||
in parentheses. The values do not have to be of the same type. Since empty
|
||||
parentheses already yield an empty array, you have to use the special `(:)`
|
||||
syntax to create an empty dictionary.
|
||||
|
||||
A dictionary is conceptually similar to an array, but it is indexed by strings
|
||||
instead of integers. You can access and create dictionary entries with the
|
||||
@ -685,12 +687,8 @@ the value. Dictionaries can be added with the `+` operator and
|
||||
To check whether a key is present in the dictionary, use the `in` keyword.
|
||||
|
||||
You can iterate over the pairs in a dictionary using a
|
||||
[for loop]($scripting/#loops).
|
||||
Dictionaries are always ordered by key.
|
||||
|
||||
Since empty parentheses already yield an empty array, you have to use the
|
||||
special `(:)` syntax to create an empty dictionary.
|
||||
|
||||
[for loop]($scripting/#loops). This will iterate in the order the pairs were
|
||||
inserted / declared.
|
||||
|
||||
## Example
|
||||
```example
|
||||
@ -735,12 +733,12 @@ If the dictionary already contains this key, the value is updated.
|
||||
The value of the pair that should be inserted.
|
||||
|
||||
### keys()
|
||||
Returns the keys of the dictionary as an array in sorted order.
|
||||
Returns the keys of the dictionary as an array in insertion order.
|
||||
|
||||
- returns: array
|
||||
|
||||
### values()
|
||||
Returns the values of the dictionary as an array in key-order.
|
||||
Returns the values of the dictionary as an array in insertion order.
|
||||
|
||||
- returns: array
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::util::{pretty_array_like, separated_list, ArcExt};
|
||||
macro_rules! __dict {
|
||||
($($key:expr => $value:expr),* $(,)?) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut map = std::collections::BTreeMap::new();
|
||||
let mut map = $crate::eval::IndexMap::new();
|
||||
$(map.insert($key.into(), $value.into());)*
|
||||
$crate::eval::Dict::from_map(map)
|
||||
}};
|
||||
@ -25,9 +25,12 @@ macro_rules! __dict {
|
||||
#[doc(inline)]
|
||||
pub use crate::__dict as dict;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use indexmap::IndexMap;
|
||||
|
||||
/// A reference-counted dictionary with value semantics.
|
||||
#[derive(Default, Clone, PartialEq, Hash)]
|
||||
pub struct Dict(Arc<BTreeMap<Str, Value>>);
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct Dict(Arc<IndexMap<Str, Value>>);
|
||||
|
||||
impl Dict {
|
||||
/// Create a new, empty dictionary.
|
||||
@ -36,7 +39,7 @@ impl Dict {
|
||||
}
|
||||
|
||||
/// Create a new dictionary from a mapping of strings to values.
|
||||
pub fn from_map(map: BTreeMap<Str, Value>) -> Self {
|
||||
pub fn from_map(map: IndexMap<Str, Value>) -> Self {
|
||||
Self(Arc::new(map))
|
||||
}
|
||||
|
||||
@ -116,7 +119,7 @@ impl Dict {
|
||||
}
|
||||
|
||||
/// Iterate over pairs of references to the contained keys and values.
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<Str, Value> {
|
||||
pub fn iter(&self) -> indexmap::map::Iter<Str, Value> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
@ -171,6 +174,15 @@ impl AddAssign for Dict {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Dict {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_usize(self.0.len());
|
||||
for item in self {
|
||||
item.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<(Str, Value)> for Dict {
|
||||
fn extend<T: IntoIterator<Item = (Str, Value)>>(&mut self, iter: T) {
|
||||
Arc::make_mut(&mut self.0).extend(iter);
|
||||
@ -185,7 +197,7 @@ impl FromIterator<(Str, Value)> for Dict {
|
||||
|
||||
impl IntoIterator for Dict {
|
||||
type Item = (Str, Value);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<Str, Value>;
|
||||
type IntoIter = indexmap::map::IntoIter<Str, Value>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Arc::take(self.0).into_iter()
|
||||
@ -194,7 +206,7 @@ impl IntoIterator for Dict {
|
||||
|
||||
impl<'a> IntoIterator for &'a Dict {
|
||||
type Item = (&'a Str, &'a Value);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, Str, Value>;
|
||||
type IntoIter = indexmap::map::Iter<'a, Str, Value>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
|
@ -37,7 +37,7 @@ pub use self::value::*;
|
||||
|
||||
pub(crate) use self::methods::methods_on;
|
||||
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -870,7 +870,7 @@ impl Eval for ast::Dict {
|
||||
type Output = Dict;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = indexmap::IndexMap::new();
|
||||
|
||||
for item in self.items() {
|
||||
match item {
|
||||
|
@ -444,6 +444,6 @@ mod tests {
|
||||
test(array![1, 2], "(1, 2)");
|
||||
test(dict![], "(:)");
|
||||
test(dict!["one" => 1], "(one: 1)");
|
||||
test(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
|
||||
test(dict!["two" => false, "one" => 1], "(two: false, one: 1)");
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
@ -48,8 +48,8 @@
|
||||
#let dict = (a: 3, c: 2, b: 1)
|
||||
#test("c" in dict, true)
|
||||
#test(dict.len(), 3)
|
||||
#test(dict.values(), (3, 1, 2))
|
||||
#test(dict.pairs().map(p => p.first() + str(p.last())).join(), "a3b1c2")
|
||||
#test(dict.values(), (3, 2, 1))
|
||||
#test(dict.pairs().map(p => p.first() + str(p.last())).join(), "a3c2b1")
|
||||
|
||||
#dict.remove("c")
|
||||
#test("c" in dict, false)
|
||||
|
@ -7,7 +7,7 @@
|
||||
// Empty array.
|
||||
#for x in () [Nope]
|
||||
|
||||
// Dictionary is not traversed in insertion order.
|
||||
// Dictionary is traversed in insertion order.
|
||||
// Should output `Age: 2. Name: Typst.`.
|
||||
#for (k, v) in (Name: "Typst", Age: 2) [
|
||||
#k: #v.
|
||||
|
Loading…
x
Reference in New Issue
Block a user