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