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