typst_library/foundations/array.rs
1use std::cmp::Ordering;
2use std::fmt::{Debug, Formatter};
3use std::num::{NonZeroI64, NonZeroUsize};
4use std::ops::{Add, AddAssign};
5
6use comemo::Tracked;
7use ecow::{EcoString, EcoVec, eco_format};
8use serde::{Deserialize, Serialize};
9use smallvec::SmallVec;
10use typst_syntax::{Span, Spanned};
11
12use crate::diag::{At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult, bail};
13use crate::engine::Engine;
14use crate::foundations::{
15 Args, Bytes, CastInfo, Context, Dict, FromValue, Func, IntoValue, Reflect, Repr, Str,
16 Value, Version, cast, func, ops, repr, scope, ty,
17};
18
19/// Create a new [`Array`] from values.
20#[macro_export]
21#[doc(hidden)]
22macro_rules! __array {
23 ($value:expr; $count:expr) => {
24 $crate::foundations::Array::from($crate::foundations::eco_vec![
25 $crate::foundations::IntoValue::into_value($value);
26 $count
27 ])
28 };
29
30 ($($value:expr),* $(,)?) => {
31 $crate::foundations::Array::from($crate::foundations::eco_vec![$(
32 $crate::foundations::IntoValue::into_value($value)
33 ),*])
34 };
35}
36
37#[doc(inline)]
38pub use crate::__array as array;
39
40/// A sequence of values.
41///
42/// You can construct an array by enclosing a comma-separated sequence of values
43/// in parentheses. The values do not have to be of the same type.
44///
45/// You can access and update array items with the `.at()` method. Indices are
46/// zero-based and negative indices wrap around to the end of the array. You can
47/// iterate over an array using a [for loop]($scripting/#loops). Arrays can be
48/// added together with the `+` operator, [joined together]($scripting/#blocks)
49/// and multiplied with integers.
50///
51/// **Note:** An array of length one needs a trailing comma, as in `{(1,)}`.
52/// This is to disambiguate from a simple parenthesized expressions like `{(1 +
53/// 2) * 3}`. An empty array is written as `{()}`.
54///
55/// # Example
56/// ```example
57/// #let values = (1, 7, 4, -3, 2)
58///
59/// #values.at(0) \
60/// #(values.at(0) = 3)
61/// #values.at(-1) \
62/// #values.find(calc.even) \
63/// #values.filter(calc.odd) \
64/// #values.map(calc.abs) \
65/// #values.rev() \
66/// #(1, (2, 3)).flatten() \
67/// #(("A", "B", "C")
68/// .join(", ", last: " and "))
69/// ```
70#[ty(scope, cast)]
71#[derive(Default, Clone, PartialEq, Hash, Serialize, Deserialize)]
72#[serde(transparent)]
73pub struct Array(EcoVec<Value>);
74
75impl Array {
76 /// Create a new, empty array.
77 pub fn new() -> Self {
78 Self::default()
79 }
80
81 /// Creates a new vec, with a known capacity.
82 pub fn with_capacity(capacity: usize) -> Self {
83 Self(EcoVec::with_capacity(capacity))
84 }
85
86 /// Return `true` if the length is 0.
87 pub fn is_empty(&self) -> bool {
88 self.0.is_empty()
89 }
90
91 /// Extract a slice of the whole array.
92 pub fn as_slice(&self) -> &[Value] {
93 self.0.as_slice()
94 }
95
96 /// Iterate over references to the contained values.
97 pub fn iter(&self) -> std::slice::Iter<'_, Value> {
98 self.0.iter()
99 }
100
101 /// Mutably borrow the first value in the array.
102 pub fn first_mut(&mut self) -> StrResult<&mut Value> {
103 self.0.make_mut().first_mut().ok_or_else(array_is_empty)
104 }
105
106 /// Mutably borrow the last value in the array.
107 pub fn last_mut(&mut self) -> StrResult<&mut Value> {
108 self.0.make_mut().last_mut().ok_or_else(array_is_empty)
109 }
110
111 /// Mutably borrow the value at the given index.
112 pub fn at_mut(&mut self, index: i64) -> StrResult<&mut Value> {
113 let len = self.len();
114 self.locate_opt(index, false)
115 .and_then(move |i| self.0.make_mut().get_mut(i))
116 .ok_or_else(|| out_of_bounds(index, len))
117 }
118
119 /// Resolve an index or throw an out of bounds error.
120 fn locate(&self, index: i64, end_ok: bool) -> StrResult<usize> {
121 self.locate_opt(index, end_ok)
122 .ok_or_else(|| out_of_bounds(index, self.len()))
123 }
124
125 /// Resolve an index, if it is within bounds.
126 ///
127 /// `index == len` is considered in bounds if and only if `end_ok` is true.
128 fn locate_opt(&self, index: i64, end_ok: bool) -> Option<usize> {
129 let wrapped =
130 if index >= 0 { Some(index) } else { (self.len() as i64).checked_add(index) };
131
132 wrapped
133 .and_then(|v| usize::try_from(v).ok())
134 .filter(|&v| v < self.0.len() + end_ok as usize)
135 }
136
137 /// Repeat this array `n` times.
138 pub fn repeat(&self, n: usize) -> StrResult<Self> {
139 let count = self
140 .len()
141 .checked_mul(n)
142 .ok_or_else(|| format!("cannot repeat this array {n} times"))?;
143
144 Ok(self.iter().cloned().cycle().take(count).collect())
145 }
146}
147
148#[scope]
149impl Array {
150 /// Converts a value to an array.
151 ///
152 /// Note that this function is only intended for conversion of a collection-like
153 /// value to an array, not for creation of an array from individual items. Use
154 /// the array syntax `(1, 2, 3)` (or `(1,)` for a single-element array) instead.
155 ///
156 /// ```example
157 /// #let hi = "Hello 😃"
158 /// #array(bytes(hi))
159 /// ```
160 #[func(constructor)]
161 pub fn construct(
162 /// The value that should be converted to an array.
163 value: ToArray,
164 ) -> Array {
165 value.0
166 }
167
168 /// The number of values in the array.
169 #[func(title = "Length")]
170 pub fn len(&self) -> usize {
171 self.0.len()
172 }
173
174 /// Returns the first item in the array. May be used on the left-hand side
175 /// an assignment. Returns the default value if the array is empty
176 /// or fails with an error is no default value was specified.
177 #[func]
178 pub fn first(
179 &self,
180 /// A default value to return if the array is empty.
181 #[named]
182 default: Option<Value>,
183 ) -> StrResult<Value> {
184 self.0.first().cloned().or(default).ok_or_else(array_is_empty)
185 }
186
187 /// Returns the last item in the array. May be used on the left-hand side of
188 /// an assignment. Returns the default value if the array is empty
189 /// or fails with an error is no default value was specified.
190 #[func]
191 pub fn last(
192 &self,
193 /// A default value to return if the array is empty.
194 #[named]
195 default: Option<Value>,
196 ) -> StrResult<Value> {
197 self.0.last().cloned().or(default).ok_or_else(array_is_empty)
198 }
199
200 /// Returns the item at the specified index in the array. May be used on the
201 /// left-hand side of an assignment. Returns the default value if the index
202 /// is out of bounds or fails with an error if no default value was
203 /// specified.
204 #[func]
205 pub fn at(
206 &self,
207 /// The index at which to retrieve the item. If negative, indexes from
208 /// the back.
209 index: i64,
210 /// A default value to return if the index is out of bounds.
211 #[named]
212 default: Option<Value>,
213 ) -> StrResult<Value> {
214 self.locate_opt(index, false)
215 .and_then(|i| self.0.get(i).cloned())
216 .or(default)
217 .ok_or_else(|| out_of_bounds_no_default(index, self.len()))
218 }
219
220 /// Adds a value to the end of the array.
221 #[func]
222 pub fn push(
223 &mut self,
224 /// The value to insert at the end of the array.
225 value: Value,
226 ) {
227 self.0.push(value);
228 }
229
230 /// Removes the last item from the array and returns it. Fails with an error
231 /// if the array is empty.
232 #[func]
233 pub fn pop(&mut self) -> StrResult<Value> {
234 self.0.pop().ok_or_else(array_is_empty)
235 }
236
237 /// Inserts a value into the array at the specified index, shifting all
238 /// subsequent elements to the right. Fails with an error if the index is
239 /// out of bounds.
240 ///
241 /// To replace an element of an array, use [`at`]($array.at).
242 #[func]
243 pub fn insert(
244 &mut self,
245 /// The index at which to insert the item. If negative, indexes from
246 /// the back.
247 index: i64,
248 /// The value to insert into the array.
249 value: Value,
250 ) -> StrResult<()> {
251 let i = self.locate(index, true)?;
252 self.0.insert(i, value);
253 Ok(())
254 }
255
256 /// Removes the value at the specified index from the array and return it.
257 #[func]
258 pub fn remove(
259 &mut self,
260 /// The index at which to remove the item. If negative, indexes from
261 /// the back.
262 index: i64,
263 /// A default value to return if the index is out of bounds.
264 #[named]
265 default: Option<Value>,
266 ) -> StrResult<Value> {
267 self.locate_opt(index, false)
268 .map(|i| self.0.remove(i))
269 .or(default)
270 .ok_or_else(|| out_of_bounds_no_default(index, self.len()))
271 }
272
273 /// Extracts a subslice of the array. Fails with an error if the start or end
274 /// index is out of bounds.
275 #[func]
276 pub fn slice(
277 &self,
278 /// The start index (inclusive). If negative, indexes from the back.
279 start: i64,
280 /// The end index (exclusive). If omitted, the whole slice until the end
281 /// of the array is extracted. If negative, indexes from the back.
282 #[default]
283 end: Option<i64>,
284 /// The number of items to extract. This is equivalent to passing
285 /// `start + count` as the `end` position. Mutually exclusive with `end`.
286 #[named]
287 count: Option<i64>,
288 ) -> StrResult<Array> {
289 let start = self.locate(start, true)?;
290 let end = end.or(count.map(|c| start as i64 + c));
291 let end = self.locate(end.unwrap_or(self.len() as i64), true)?.max(start);
292 Ok(self.0[start..end].into())
293 }
294
295 /// Whether the array contains the specified value.
296 ///
297 /// This method also has dedicated syntax: You can write `{2 in (1, 2, 3)}`
298 /// instead of `{(1, 2, 3).contains(2)}`.
299 #[func]
300 pub fn contains(
301 &self,
302 /// The value to search for.
303 value: Value,
304 ) -> bool {
305 self.0.contains(&value)
306 }
307
308 /// Searches for an item for which the given function returns `{true}` and
309 /// returns the first match or `{none}` if there is no match.
310 #[func]
311 pub fn find(
312 &self,
313 engine: &mut Engine,
314 context: Tracked<Context>,
315 /// The function to apply to each item. Must return a boolean.
316 searcher: Func,
317 ) -> SourceResult<Option<Value>> {
318 for item in self.iter() {
319 if searcher
320 .call(engine, context, [item.clone()])?
321 .cast::<bool>()
322 .at(searcher.span())?
323 {
324 return Ok(Some(item.clone()));
325 }
326 }
327 Ok(None)
328 }
329
330 /// Searches for an item for which the given function returns `{true}` and
331 /// returns the index of the first match or `{none}` if there is no match.
332 #[func]
333 pub fn position(
334 &self,
335 engine: &mut Engine,
336 context: Tracked<Context>,
337 /// The function to apply to each item. Must return a boolean.
338 searcher: Func,
339 ) -> SourceResult<Option<i64>> {
340 for (i, item) in self.iter().enumerate() {
341 if searcher
342 .call(engine, context, [item.clone()])?
343 .cast::<bool>()
344 .at(searcher.span())?
345 {
346 return Ok(Some(i as i64));
347 }
348 }
349
350 Ok(None)
351 }
352
353 /// Create an array consisting of a sequence of numbers.
354 ///
355 /// If you pass just one positional parameter, it is interpreted as the
356 /// `end` of the range. If you pass two, they describe the `start` and `end`
357 /// of the range.
358 ///
359 /// This function is available both in the array function's scope and
360 /// globally.
361 ///
362 /// ```example
363 /// #range(5) \
364 /// #range(2, 5) \
365 /// #range(20, step: 4) \
366 /// #range(21, step: 4) \
367 /// #range(5, 2, step: -1)
368 /// ```
369 #[func]
370 pub fn range(
371 args: &mut Args,
372 /// The start of the range (inclusive).
373 #[external]
374 #[default]
375 start: i64,
376 /// The end of the range (exclusive).
377 #[external]
378 end: i64,
379 /// The distance between the generated numbers.
380 #[named]
381 #[default(NonZeroI64::new(1).unwrap())]
382 step: NonZeroI64,
383 ) -> SourceResult<Array> {
384 let first = args.expect::<i64>("end")?;
385 let (start, end) = match args.eat::<i64>()? {
386 Some(second) => (first, second),
387 None => (0, first),
388 };
389
390 let step = step.get();
391
392 let mut x = start;
393 let mut array = Self::new();
394
395 while x.cmp(&end) == 0.cmp(&step) {
396 array.push(x.into_value());
397 x += step;
398 }
399
400 Ok(array)
401 }
402
403 /// Produces a new array with only the items from the original one for which
404 /// the given function returns true.
405 #[func]
406 pub fn filter(
407 &self,
408 engine: &mut Engine,
409 context: Tracked<Context>,
410 /// The function to apply to each item. Must return a boolean.
411 test: Func,
412 ) -> SourceResult<Array> {
413 let mut kept = EcoVec::new();
414 for item in self.iter() {
415 if test
416 .call(engine, context, [item.clone()])?
417 .cast::<bool>()
418 .at(test.span())?
419 {
420 kept.push(item.clone())
421 }
422 }
423 Ok(kept.into())
424 }
425
426 /// Produces a new array in which all items from the original one were
427 /// transformed with the given function.
428 #[func]
429 pub fn map(
430 self,
431 engine: &mut Engine,
432 context: Tracked<Context>,
433 /// The function to apply to each item.
434 mapper: Func,
435 ) -> SourceResult<Array> {
436 self.into_iter()
437 .map(|item| mapper.call(engine, context, [item]))
438 .collect()
439 }
440
441 /// Returns a new array with the values alongside their indices.
442 ///
443 /// The returned array consists of `(index, value)` pairs in the form of
444 /// length-2 arrays. These can be [destructured]($scripting/#bindings) with
445 /// a let binding or for loop.
446 ///
447 /// ```example
448 /// #for (i, value) in ("A", "B", "C").enumerate() {
449 /// [#i: #value \ ]
450 /// }
451 ///
452 /// #("A", "B", "C").enumerate(start: 1)
453 /// ```
454 #[func]
455 pub fn enumerate(
456 self,
457 /// The index returned for the first pair of the returned list.
458 #[named]
459 #[default(0)]
460 start: i64,
461 ) -> StrResult<Array> {
462 self.into_iter()
463 .enumerate()
464 .map(|(i, value)| {
465 Ok(array![
466 start
467 .checked_add_unsigned(i as u64)
468 .ok_or("array index is too large")?,
469 value
470 ]
471 .into_value())
472 })
473 .collect()
474 }
475
476 /// Zips the array with other arrays.
477 ///
478 /// Returns an array of arrays, where the `i`th inner array contains all the
479 /// `i`th elements from each original array.
480 ///
481 /// If the arrays to be zipped have different lengths, they are zipped up to
482 /// the last element of the shortest array and all remaining elements are
483 /// ignored.
484 ///
485 /// This function is variadic, meaning that you can zip multiple arrays
486 /// together at once: `{(1, 2).zip(("A", "B"), (10, 20))}` yields
487 /// `{((1, "A", 10), (2, "B", 20))}`.
488 #[func]
489 pub fn zip(
490 self,
491 args: &mut Args,
492 /// Whether all arrays have to have the same length.
493 /// For example, `{(1, 2).zip((1, 2, 3), exact: true)}` produces an
494 /// error.
495 #[named]
496 #[default(false)]
497 exact: bool,
498 /// The arrays to zip with.
499 #[external]
500 #[variadic]
501 others: Vec<Array>,
502 ) -> SourceResult<Array> {
503 let remaining = args.remaining();
504
505 // Fast path for one array.
506 if remaining == 0 {
507 return Ok(self.into_iter().map(|item| array![item].into_value()).collect());
508 }
509
510 // Fast path for just two arrays.
511 if remaining == 1 {
512 let Spanned { v: other, span: other_span } =
513 args.expect::<Spanned<Array>>("others")?;
514 if exact && self.len() != other.len() {
515 bail!(
516 other_span,
517 "second array has different length ({}) from first array ({})",
518 other.len(),
519 self.len()
520 );
521 }
522 return Ok(self
523 .into_iter()
524 .zip(other)
525 .map(|(first, second)| array![first, second].into_value())
526 .collect());
527 }
528
529 // If there is more than one array, we use the manual method.
530 let mut out = Self::with_capacity(self.len());
531 let arrays = args.all::<Spanned<Array>>()?;
532 if exact {
533 let errs = arrays
534 .iter()
535 .filter(|sp| sp.v.len() != self.len())
536 .map(|Spanned { v, span }| {
537 SourceDiagnostic::error(
538 *span,
539 eco_format!(
540 "array has different length ({}) from first array ({})",
541 v.len(),
542 self.len()
543 ),
544 )
545 })
546 .collect::<EcoVec<_>>();
547 if !errs.is_empty() {
548 return Err(errs);
549 }
550 }
551
552 let mut iterators =
553 arrays.into_iter().map(|i| i.v.into_iter()).collect::<Vec<_>>();
554
555 for this in self {
556 let mut row = Self::with_capacity(1 + iterators.len());
557 row.push(this.clone());
558
559 for iterator in &mut iterators {
560 let Some(item) = iterator.next() else {
561 return Ok(out);
562 };
563
564 row.push(item);
565 }
566
567 out.push(row.into_value());
568 }
569
570 Ok(out)
571 }
572
573 /// Folds all items into a single value using an accumulator function.
574 ///
575 /// ```example
576 /// #let array = (1, 2, 3, 4)
577 /// #array.fold(0, (acc, x) => acc + x)
578 /// ```
579 #[func]
580 pub fn fold(
581 self,
582 engine: &mut Engine,
583 context: Tracked<Context>,
584 /// The initial value to start with.
585 init: Value,
586 /// The folding function. Must have two parameters: One for the
587 /// accumulated value and one for an item.
588 folder: Func,
589 ) -> SourceResult<Value> {
590 let mut acc = init;
591 for item in self {
592 acc = folder.call(engine, context, [acc, item])?;
593 }
594 Ok(acc)
595 }
596
597 /// Sums all items (works for all types that can be added).
598 #[func]
599 pub fn sum(
600 self,
601 /// What to return if the array is empty. Must be set if the array can
602 /// be empty.
603 #[named]
604 default: Option<Value>,
605 ) -> HintedStrResult<Value> {
606 let mut iter = self.into_iter();
607 let mut acc = iter
608 .next()
609 .or(default)
610 .ok_or("cannot calculate sum of empty array with no default")?;
611 for item in iter {
612 acc = ops::add(acc, item)?;
613 }
614 Ok(acc)
615 }
616
617 /// Calculates the product of all items (works for all types that can be
618 /// multiplied).
619 #[func]
620 pub fn product(
621 self,
622 /// What to return if the array is empty. Must be set if the array can
623 /// be empty.
624 #[named]
625 default: Option<Value>,
626 ) -> HintedStrResult<Value> {
627 let mut iter = self.into_iter();
628 let mut acc = iter
629 .next()
630 .or(default)
631 .ok_or("cannot calculate product of empty array with no default")?;
632 for item in iter {
633 acc = ops::mul(acc, item)?;
634 }
635 Ok(acc)
636 }
637
638 /// Whether the given function returns `{true}` for any item in the array.
639 #[func]
640 pub fn any(
641 self,
642 engine: &mut Engine,
643 context: Tracked<Context>,
644 /// The function to apply to each item. Must return a boolean.
645 test: Func,
646 ) -> SourceResult<bool> {
647 for item in self {
648 if test.call(engine, context, [item])?.cast::<bool>().at(test.span())? {
649 return Ok(true);
650 }
651 }
652
653 Ok(false)
654 }
655
656 /// Whether the given function returns `{true}` for all items in the array.
657 #[func]
658 pub fn all(
659 self,
660 engine: &mut Engine,
661 context: Tracked<Context>,
662 /// The function to apply to each item. Must return a boolean.
663 test: Func,
664 ) -> SourceResult<bool> {
665 for item in self {
666 if !test.call(engine, context, [item])?.cast::<bool>().at(test.span())? {
667 return Ok(false);
668 }
669 }
670
671 Ok(true)
672 }
673
674 /// Combine all nested arrays into a single flat one.
675 #[func]
676 pub fn flatten(self) -> Array {
677 let mut flat = EcoVec::with_capacity(self.0.len());
678 for item in self {
679 if let Value::Array(nested) = item {
680 flat.extend(nested.flatten());
681 } else {
682 flat.push(item);
683 }
684 }
685 flat.into()
686 }
687
688 /// Return a new array with the same items, but in reverse order.
689 #[func(title = "Reverse")]
690 pub fn rev(self) -> Array {
691 self.into_iter().rev().collect()
692 }
693
694 /// Split the array at occurrences of the specified value.
695 ///
696 /// ```example
697 /// #(1, 1, 2, 3, 2, 4, 5).split(2)
698 /// ```
699 #[func]
700 pub fn split(
701 &self,
702 /// The value to split at.
703 at: Value,
704 ) -> Array {
705 self.as_slice()
706 .split(|value| *value == at)
707 .map(|subslice| Value::Array(subslice.iter().cloned().collect()))
708 .collect()
709 }
710
711 /// Combine all items in the array into one.
712 #[func]
713 pub fn join(
714 self,
715 /// A value to insert between each item of the array.
716 #[default]
717 separator: Option<Value>,
718 /// An alternative separator between the last two items.
719 #[named]
720 last: Option<Value>,
721 /// What to return if the array is empty.
722 #[named]
723 default: Option<Value>,
724 ) -> StrResult<Value> {
725 let len = self.0.len();
726
727 if let Some(result) = default
728 && len == 0
729 {
730 return Ok(result);
731 }
732
733 let separator = separator.unwrap_or(Value::None);
734
735 let mut last = last;
736 let mut result = Value::None;
737 for (i, value) in self.into_iter().enumerate() {
738 if i > 0 {
739 if i + 1 == len && last.is_some() {
740 result = ops::join(result, last.take().unwrap())?;
741 } else {
742 result = ops::join(result, separator.clone())?;
743 }
744 }
745
746 result = ops::join(result, value)?;
747 }
748
749 Ok(result)
750 }
751
752 /// Returns an array with a copy of the separator value placed between
753 /// adjacent elements.
754 ///
755 /// ```example
756 /// #("A", "B", "C").intersperse("-")
757 /// ```
758 #[func]
759 pub fn intersperse(
760 self,
761 /// The value that will be placed between each adjacent element.
762 separator: Value,
763 ) -> Array {
764 // TODO: Use once stabilized:
765 // https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.intersperse
766 let size = match self.len() {
767 0 => return Array::new(),
768 n => (2 * n) - 1,
769 };
770 let mut vec = EcoVec::with_capacity(size);
771 let mut iter = self.into_iter();
772
773 if let Some(first) = iter.next() {
774 vec.push(first);
775 }
776
777 for value in iter {
778 vec.push(separator.clone());
779 vec.push(value);
780 }
781
782 Array(vec)
783 }
784
785 /// Splits an array into non-overlapping chunks, starting at the beginning,
786 /// ending with a single remainder chunk.
787 ///
788 /// All chunks but the last have `chunk-size` elements.
789 /// If `exact` is set to `{true}`, the remainder is dropped if it
790 /// contains less than `chunk-size` elements.
791 ///
792 /// ```example
793 /// #let array = (1, 2, 3, 4, 5, 6, 7, 8)
794 /// #array.chunks(3) \
795 /// #array.chunks(3, exact: true)
796 /// ```
797 #[func]
798 pub fn chunks(
799 self,
800 /// How many elements each chunk may at most contain.
801 chunk_size: NonZeroUsize,
802 /// Whether to keep the remainder if its size is less than `chunk-size`.
803 #[named]
804 #[default(false)]
805 exact: bool,
806 ) -> Array {
807 let to_array = |chunk| Array::from(chunk).into_value();
808 if exact {
809 self.0.chunks_exact(chunk_size.get()).map(to_array).collect()
810 } else {
811 self.0.chunks(chunk_size.get()).map(to_array).collect()
812 }
813 }
814
815 /// Returns sliding windows of `window-size` elements over an array.
816 ///
817 /// If the array length is less than `window-size`, this will return an empty array.
818 ///
819 /// ```example
820 /// #let array = (1, 2, 3, 4, 5, 6, 7, 8)
821 /// #array.windows(5)
822 /// ```
823 #[func]
824 pub fn windows(
825 self,
826 /// How many elements each window will contain.
827 window_size: NonZeroUsize,
828 ) -> Array {
829 self.0
830 .windows(window_size.get())
831 .map(|window| Array::from(window).into_value())
832 .collect()
833 }
834
835 /// Return a sorted version of this array, optionally by a given key
836 /// function. The sorting algorithm used is stable.
837 ///
838 /// Returns an error if two values could not be compared or if the key
839 /// or comparison function (if given) yields an error.
840 ///
841 /// To sort according to multiple criteria at once, e.g. in case of equality
842 /// between some criteria, the key function can return an array. The results
843 /// are in lexicographic order.
844 ///
845 /// ```example
846 /// #let array = (
847 /// (a: 2, b: 4),
848 /// (a: 1, b: 5),
849 /// (a: 2, b: 3),
850 /// )
851 /// #array.sorted(key: it => (it.a, it.b))
852 /// ```
853 #[func]
854 pub fn sorted(
855 self,
856 engine: &mut Engine,
857 context: Tracked<Context>,
858 span: Span,
859 /// If given, applies this function to each element in the array to
860 /// determine the keys to sort by.
861 #[named]
862 key: Option<Func>,
863 /// If given, uses this function to compare every two elements in the
864 /// array.
865 ///
866 /// The function will receive two elements in the array for comparison,
867 /// and should return a boolean indicating their order: `{true}`
868 /// indicates that the elements are in order, while `{false}` indicates
869 /// that they should be swapped. To keep the sort stable, if the two
870 /// elements are equal, the function should return `{true}`.
871 ///
872 /// If this function does not order the elements properly (e.g., by
873 /// returning `{false}` for both `{(x, y)}` and `{(y, x)}`, or for
874 /// `{(x, x)}`), the resulting array will be in unspecified order.
875 ///
876 /// When used together with `key`, `by` will be passed the keys instead
877 /// of the elements.
878 ///
879 /// ```example
880 /// #(
881 /// "sorted",
882 /// "by",
883 /// "decreasing",
884 /// "length",
885 /// ).sorted(
886 /// key: s => s.len(),
887 /// by: (l, r) => l >= r,
888 /// )
889 /// ```
890 #[named]
891 by: Option<Func>,
892 ) -> SourceResult<Array> {
893 match by {
894 Some(by) => {
895 let mut are_in_order = |mut x, mut y| {
896 if let Some(f) = &key {
897 // We rely on `comemo`'s memoization of function
898 // evaluation to not excessively reevaluate the key.
899 x = f.call(engine, context, [x])?;
900 y = f.call(engine, context, [y])?;
901 }
902 match by.call(engine, context, [x, y])? {
903 Value::Bool(b) => Ok(b),
904 x => {
905 bail!(
906 span,
907 "expected boolean from `by` function, got {}",
908 x.ty(),
909 )
910 }
911 }
912 };
913 // If a comparison function is provided, we use `glidesort`
914 // instead of the standard library sorting algorithm to prevent
915 // panics in case the comparison function does not define a
916 // valid order (see https://github.com/typst/typst/pull/5627).
917 let mut result = Ok(());
918 let mut vec = self.0.into_iter().enumerate().collect::<Vec<_>>();
919 glidesort::sort_by(&mut vec, |(i, x), (j, y)| {
920 // Because we use booleans for the comparison function, in
921 // order to keep the sort stable, we need to compare in the
922 // right order.
923 if i < j {
924 // If `x` and `y` appear in this order in the original
925 // array, then we should change their order (i.e.,
926 // return `Ordering::Greater`) iff `y` is strictly less
927 // than `x` (i.e., `compare(x, y)` returns `false`).
928 // Otherwise, we should keep them in the same order
929 // (i.e., return `Ordering::Less`).
930 match are_in_order(x.clone(), y.clone()) {
931 Ok(false) => Ordering::Greater,
932 Ok(true) => Ordering::Less,
933 Err(err) => {
934 if result.is_ok() {
935 result = Err(err);
936 }
937 Ordering::Equal
938 }
939 }
940 } else {
941 // If `x` and `y` appear in the opposite order in the
942 // original array, then we should change their order
943 // (i.e., return `Ordering::Less`) iff `x` is strictly
944 // less than `y` (i.e., `compare(y, x)` returns
945 // `false`). Otherwise, we should keep them in the same
946 // order (i.e., return `Ordering::Less`).
947 match are_in_order(y.clone(), x.clone()) {
948 Ok(false) => Ordering::Less,
949 Ok(true) => Ordering::Greater,
950 Err(err) => {
951 if result.is_ok() {
952 result = Err(err);
953 }
954 Ordering::Equal
955 }
956 }
957 }
958 });
959 result.map(|()| vec.into_iter().map(|(_, x)| x).collect())
960 }
961
962 None => {
963 let mut key_of = |x: Value| match &key {
964 // We rely on `comemo`'s memoization of function evaluation
965 // to not excessively reevaluate the key.
966 Some(f) => f.call(engine, context, [x]),
967 None => Ok(x),
968 };
969 // If no comparison function is provided, we know the order is
970 // valid, so we can use the standard library sort and prevent an
971 // extra allocation.
972 let mut result = Ok(());
973 let mut vec = self.0;
974 vec.make_mut().sort_by(|a, b| {
975 match (key_of(a.clone()), key_of(b.clone())) {
976 (Ok(a), Ok(b)) => ops::compare(&a, &b).unwrap_or_else(|err| {
977 if result.is_ok() {
978 result = Err(err).at(span);
979 }
980 Ordering::Equal
981 }),
982 (Err(e), _) | (_, Err(e)) => {
983 if result.is_ok() {
984 result = Err(e);
985 }
986 Ordering::Equal
987 }
988 }
989 });
990 result.map(|()| vec.into())
991 }
992 }
993 }
994
995 /// Deduplicates all items in the array.
996 ///
997 /// Returns a new array with all duplicate items removed. Only the first
998 /// element of each duplicate is kept.
999 ///
1000 /// ```example
1001 /// #(3, 3, 1, 2, 3).dedup()
1002 /// ```
1003 #[func(title = "Deduplicate")]
1004 pub fn dedup(
1005 self,
1006 engine: &mut Engine,
1007 context: Tracked<Context>,
1008 /// If given, applies this function to each element in the array to
1009 /// determine the keys to deduplicate by.
1010 ///
1011 /// ```example
1012 /// #("apple", "banana", " apple ").dedup(key: s => s.trim())
1013 /// ```
1014 #[named]
1015 key: Option<Func>,
1016 ) -> SourceResult<Array> {
1017 let mut out = EcoVec::with_capacity(self.0.len());
1018 let mut key_of = |x: Value| match &key {
1019 // NOTE: We are relying on `comemo`'s memoization of function
1020 // evaluation to not excessively reevaluate the `key`.
1021 Some(f) => f.call(engine, context, [x]),
1022 None => Ok(x),
1023 };
1024
1025 // This algorithm is O(N^2) because we cannot rely on `HashSet` since:
1026 // 1. We would like to preserve the order of the elements.
1027 // 2. We cannot hash arbitrary `Value`.
1028 'outer: for value in self {
1029 let key = key_of(value.clone())?;
1030 if out.is_empty() {
1031 out.push(value);
1032 continue;
1033 }
1034
1035 for second in out.iter() {
1036 if ops::equal(&key, &key_of(second.clone())?) {
1037 continue 'outer;
1038 }
1039 }
1040
1041 out.push(value);
1042 }
1043
1044 Ok(Self(out))
1045 }
1046
1047 /// Converts an array of pairs into a dictionary.
1048 /// The first value of each pair is the key, the second the value.
1049 ///
1050 /// If the same key occurs multiple times, the last value is selected.
1051 ///
1052 /// ```example
1053 /// #(
1054 /// ("apples", 2),
1055 /// ("peaches", 3),
1056 /// ("apples", 5),
1057 /// ).to-dict()
1058 /// ```
1059 #[func]
1060 pub fn to_dict(self) -> StrResult<Dict> {
1061 self.into_iter()
1062 .map(|value| {
1063 let value_ty = value.ty();
1064 let pair = value.cast::<Array>().map_err(|_| {
1065 eco_format!("expected (str, any) pairs, found {}", value_ty)
1066 })?;
1067 if let [key, value] = pair.as_slice() {
1068 let key = key.clone().cast::<Str>().map_err(|_| {
1069 eco_format!("expected key of type str, found {}", value.ty())
1070 })?;
1071 Ok((key, value.clone()))
1072 } else {
1073 bail!("expected pairs of length 2, found length {}", pair.len());
1074 }
1075 })
1076 .collect()
1077 }
1078
1079 /// Reduces the elements to a single one, by repeatedly applying a reducing
1080 /// operation.
1081 ///
1082 /// If the array is empty, returns `{none}`, otherwise, returns the result
1083 /// of the reduction.
1084 ///
1085 /// The reducing function is a closure with two arguments: an "accumulator",
1086 /// and an element.
1087 ///
1088 /// For arrays with at least one element, this is the same as [`array.fold`]
1089 /// with the first element of the array as the initial accumulator value,
1090 /// folding every subsequent element into it.
1091 ///
1092 /// ```example
1093 /// #let array = (2, 1, 4, 3)
1094 /// #array.reduce((acc, x) => calc.max(acc, x))
1095 /// ```
1096 #[func]
1097 pub fn reduce(
1098 self,
1099 engine: &mut Engine,
1100 context: Tracked<Context>,
1101 /// The reducing function. Must have two parameters: One for the
1102 /// accumulated value and one for an item.
1103 reducer: Func,
1104 ) -> SourceResult<Value> {
1105 let mut iter = self.into_iter();
1106 let mut acc = iter.next().unwrap_or_default();
1107 for item in iter {
1108 acc = reducer.call(engine, context, [acc, item])?;
1109 }
1110 Ok(acc)
1111 }
1112}
1113
1114/// A value that can be cast to bytes.
1115pub struct ToArray(Array);
1116
1117cast! {
1118 ToArray,
1119 v: Array => Self(v),
1120 v: Bytes => Self(v.iter().map(|&b| Value::Int(b.into())).collect()),
1121 v: Version => Self(v.values().iter().map(|&v| Value::Int(v as i64)).collect())
1122}
1123
1124impl Debug for Array {
1125 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1126 f.debug_list().entries(&self.0).finish()
1127 }
1128}
1129
1130impl Repr for Array {
1131 fn repr(&self) -> EcoString {
1132 let max = 40;
1133 let mut pieces: Vec<_> = self
1134 .iter()
1135 .take(max)
1136 .map(|value| eco_format!("{}", value.repr()))
1137 .collect();
1138 if self.len() > max {
1139 pieces.push(eco_format!(".. ({} items omitted)", self.len() - max));
1140 }
1141 repr::pretty_array_like(&pieces, self.len() == 1).into()
1142 }
1143}
1144
1145impl Add for Array {
1146 type Output = Self;
1147
1148 fn add(mut self, rhs: Array) -> Self::Output {
1149 self += rhs;
1150 self
1151 }
1152}
1153
1154impl AddAssign for Array {
1155 fn add_assign(&mut self, rhs: Self) {
1156 self.0.extend(rhs.0);
1157 }
1158}
1159
1160impl Extend<Value> for Array {
1161 fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) {
1162 self.0.extend(iter);
1163 }
1164}
1165
1166impl FromIterator<Value> for Array {
1167 fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
1168 Self(iter.into_iter().collect())
1169 }
1170}
1171
1172impl IntoIterator for Array {
1173 type Item = Value;
1174 type IntoIter = ecow::vec::IntoIter<Value>;
1175
1176 fn into_iter(self) -> Self::IntoIter {
1177 self.0.into_iter()
1178 }
1179}
1180
1181impl<'a> IntoIterator for &'a Array {
1182 type Item = &'a Value;
1183 type IntoIter = std::slice::Iter<'a, Value>;
1184
1185 fn into_iter(self) -> Self::IntoIter {
1186 self.iter()
1187 }
1188}
1189
1190impl From<EcoVec<Value>> for Array {
1191 fn from(v: EcoVec<Value>) -> Self {
1192 Array(v)
1193 }
1194}
1195
1196impl From<&[Value]> for Array {
1197 fn from(v: &[Value]) -> Self {
1198 Array(v.into())
1199 }
1200}
1201
1202impl<T> Reflect for Vec<T> {
1203 fn input() -> CastInfo {
1204 Array::input()
1205 }
1206
1207 fn output() -> CastInfo {
1208 Array::output()
1209 }
1210
1211 fn castable(value: &Value) -> bool {
1212 Array::castable(value)
1213 }
1214}
1215
1216impl<T: Reflect, const N: usize> Reflect for SmallVec<[T; N]> {
1217 fn input() -> CastInfo {
1218 Array::input()
1219 }
1220
1221 fn output() -> CastInfo {
1222 Array::output()
1223 }
1224
1225 fn castable(value: &Value) -> bool {
1226 Array::castable(value)
1227 }
1228}
1229
1230impl<T: IntoValue> IntoValue for Vec<T> {
1231 fn into_value(self) -> Value {
1232 Value::Array(self.into_iter().map(IntoValue::into_value).collect())
1233 }
1234}
1235
1236impl<T: IntoValue, const N: usize> IntoValue for SmallVec<[T; N]> {
1237 fn into_value(self) -> Value {
1238 Value::Array(self.into_iter().map(IntoValue::into_value).collect())
1239 }
1240}
1241
1242impl<T: FromValue> FromValue for Vec<T> {
1243 fn from_value(value: Value) -> HintedStrResult<Self> {
1244 value.cast::<Array>()?.into_iter().map(Value::cast).collect()
1245 }
1246}
1247
1248impl<T: FromValue, const N: usize> FromValue for SmallVec<[T; N]> {
1249 fn from_value(value: Value) -> HintedStrResult<Self> {
1250 value.cast::<Array>()?.into_iter().map(Value::cast).collect()
1251 }
1252}
1253
1254/// One element, or multiple provided as an array.
1255#[derive(Debug, Clone, PartialEq, Hash)]
1256pub struct OneOrMultiple<T>(pub Vec<T>);
1257
1258impl<T: Reflect> Reflect for OneOrMultiple<T> {
1259 fn input() -> CastInfo {
1260 T::input() + Array::input()
1261 }
1262
1263 fn output() -> CastInfo {
1264 T::output() + Array::output()
1265 }
1266
1267 fn castable(value: &Value) -> bool {
1268 Array::castable(value) || T::castable(value)
1269 }
1270}
1271
1272impl<T: IntoValue + Clone> IntoValue for OneOrMultiple<T> {
1273 fn into_value(self) -> Value {
1274 self.0.into_value()
1275 }
1276}
1277
1278impl<T: FromValue> FromValue for OneOrMultiple<T> {
1279 fn from_value(value: Value) -> HintedStrResult<Self> {
1280 if T::castable(&value) {
1281 return Ok(Self(vec![T::from_value(value)?]));
1282 }
1283 if Array::castable(&value) {
1284 return Ok(Self(
1285 Array::from_value(value)?
1286 .into_iter()
1287 .map(|value| T::from_value(value))
1288 .collect::<HintedStrResult<_>>()?,
1289 ));
1290 }
1291 Err(Self::error(&value))
1292 }
1293}
1294
1295impl<T> Default for OneOrMultiple<T> {
1296 fn default() -> Self {
1297 Self(vec![])
1298 }
1299}
1300
1301/// The error message when the array is empty.
1302#[cold]
1303fn array_is_empty() -> EcoString {
1304 "array is empty".into()
1305}
1306
1307/// The out of bounds access error message.
1308#[cold]
1309fn out_of_bounds(index: i64, len: usize) -> EcoString {
1310 eco_format!("array index out of bounds (index: {index}, len: {len})")
1311}
1312
1313/// The out of bounds access error message when no default value was given.
1314#[cold]
1315fn out_of_bounds_no_default(index: i64, len: usize) -> EcoString {
1316 eco_format!(
1317 "array index out of bounds (index: {index}, len: {len}) \
1318 and no default value was specified",
1319 )
1320}