1use std::collections::HashMap;
4use std::hash::Hash;
5use std::mem;
6
7use crate::*;
8
9pub fn map_keyed<T, K, U>(
25 list: impl Into<MaybeDyn<Vec<T>>> + 'static,
26 mut map_fn: impl FnMut(T) -> U + 'static,
27 key_fn: impl Fn(&T) -> K + 'static,
28) -> ReadSignal<Vec<U>>
29where
30 T: PartialEq + Clone + 'static,
31 K: Eq + Hash,
32 U: Clone,
33{
34 let list = list.into();
35 let mut items = Vec::new();
37
38 let mut mapped: Vec<U> = Vec::new();
39 let mut mapped_tmp: Vec<Option<U>> = Vec::new();
40
41 let mut disposers: Vec<Option<NodeHandle>> = Vec::new();
42 let mut disposers_tmp: Vec<Option<NodeHandle>> = Vec::new();
43
44 let mut update = move || {
46 let new_items = list.get_clone();
47 if new_items.is_empty() {
48 for dis in mem::take(&mut disposers) {
50 dis.unwrap().dispose();
51 }
52 mapped = Vec::new();
53 } else if items.is_empty() {
54 mapped.reserve(new_items.len());
56 disposers.reserve(new_items.len());
57
58 for new_item in new_items.iter().cloned() {
59 let map_fn = &mut map_fn;
60 let mapped = &mut mapped;
61 let new_disposer = create_child_scope(move || mapped.push(map_fn(new_item)));
62 disposers.push(Some(new_disposer));
63 }
64 } else {
65 mapped_tmp.clear();
66 mapped_tmp.resize(new_items.len(), None);
67
68 disposers_tmp.clear();
69 disposers_tmp.resize_with(new_items.len(), || None);
70
71 let min_len = usize::min(items.len(), new_items.len());
73 let start = items
74 .iter()
75 .zip(new_items.iter())
76 .position(|(a, b)| a != b)
77 .unwrap_or(min_len);
78 debug_assert!(
79 (items.get(start).is_none() && new_items.get(start).is_none())
80 || (items.get(start) != new_items.get(start)),
81 "start is the first index where items[start] != new_items[start]"
82 );
83
84 let mut end = items.len();
86 let mut new_end = new_items.len();
87 while end > start && new_end > start && items[end - 1] == new_items[new_end - 1] {
88 end -= 1;
89 new_end -= 1;
90 mapped_tmp[new_end] = Some(mapped[end].clone());
91 disposers_tmp[new_end] = disposers[end].take();
92 }
93 debug_assert!(
94 if end != 0 && new_end != 0 {
95 (end == items.len() && new_end == new_items.len())
96 || (items[end - 1] != new_items[new_end - 1])
97 } else {
98 true
99 },
100 "end and new_end are the last indexes where items[end - 1] != new_items[new_end - 1]"
101 );
102
103 let mut new_indices = HashMap::with_capacity(new_end - start);
106
107 let mut new_indices_next = vec![None; new_end - start];
110 for j in (start..new_end).rev() {
111 let item = &new_items[j];
112 let key = key_fn(item);
113 let i = new_indices.get(&key);
114 new_indices_next[j - start] = i.copied();
115 new_indices.insert(key, j);
116 }
117
118 for i in start..end {
121 let item = &items[i];
122 let key = key_fn(item);
123 if let Some(j) = new_indices.get(&key).copied() {
124 mapped_tmp[j] = Some(mapped[i].clone());
126 disposers_tmp[j] = disposers[i].take();
127 new_indices_next[j - start].and_then(|j| new_indices.insert(key, j));
128 } else {
129 disposers[i].take().unwrap().dispose();
131 }
132 }
133
134 for j in start..new_items.len() {
137 if matches!(mapped_tmp.get(j), Some(Some(_))) {
138 if j >= mapped.len() {
140 debug_assert_eq!(mapped.len(), j);
141 mapped.push(mapped_tmp[j].clone().unwrap());
142 disposers.push(disposers_tmp[j].take());
143 } else {
144 mapped[j] = mapped_tmp[j].clone().unwrap();
145 disposers[j] = disposers_tmp[j].take();
146 }
147 } else {
148 let mut tmp = None;
150 let new_item = new_items[j].clone();
151 let new_disposer = create_child_scope(|| tmp = Some(map_fn(new_item)));
152 if mapped.len() > j {
153 mapped[j] = tmp.unwrap();
154 disposers[j] = Some(new_disposer);
155 } else {
156 mapped.push(tmp.unwrap());
157 disposers.push(Some(new_disposer));
158 }
159 }
160 }
161 }
162
163 mapped.truncate(new_items.len());
165 disposers.truncate(new_items.len());
166
167 debug_assert!([mapped.len(), disposers.len()]
169 .iter()
170 .all(|l| *l == new_items.len()));
171 items = new_items;
172
173 mapped.clone()
174 };
175 let scope = use_current_scope();
176 create_memo(move || scope.run_in(&mut update))
177}
178
179pub fn map_indexed<T, U>(
195 list: impl Into<MaybeDyn<Vec<T>>> + 'static,
196 mut map_fn: impl FnMut(T) -> U + 'static,
197) -> ReadSignal<Vec<U>>
198where
199 T: PartialEq + Clone + 'static,
200 U: Clone,
201{
202 let list = list.into();
203 let mut items = Vec::new();
205 let mut mapped = Vec::new();
206 let mut disposers: Vec<NodeHandle> = Vec::new();
207
208 let mut update = move || {
210 let new_items = list.get_clone();
211
212 if new_items.is_empty() {
213 for dis in mem::take(&mut disposers) {
215 dis.dispose();
216 }
217 items = Vec::new();
218 mapped = Vec::new();
219 } else {
220 if new_items.len() > items.len() {
222 let new_count = new_items.len() - items.len();
223 mapped.reserve(new_count);
224 disposers.reserve(new_count);
225 }
226
227 for (i, new_item) in new_items.iter().cloned().enumerate() {
228 let item = items.get(i);
229 let eqs = item != Some(&new_item);
231
232 if item.is_none() || eqs {
233 let mut tmp = None;
234 let new_disposer = create_child_scope(|| tmp = Some(map_fn(new_item)));
235 if item.is_none() {
236 mapped.push(tmp.unwrap());
237 disposers.push(new_disposer);
238 } else if eqs {
239 mapped[i] = tmp.unwrap();
240 let prev = mem::replace(&mut disposers[i], new_disposer);
241 prev.dispose();
242 }
243 }
244 }
245
246 if new_items.len() < items.len() {
247 for _i in new_items.len()..items.len() {
248 disposers.pop().unwrap().dispose();
249 }
250 }
251
252 mapped.truncate(new_items.len());
254
255 debug_assert!([mapped.len(), disposers.len()]
257 .iter()
258 .all(|l| *l == new_items.len()));
259 items = new_items;
260 }
261
262 mapped.clone()
264 };
265 let scope = use_current_scope();
266 create_memo(move || scope.run_in(&mut update))
267}
268
269#[cfg(test)]
270mod tests {
271 use std::cell::Cell;
272 use std::rc::Rc;
273
274 use super::*;
275
276 #[test]
277 fn keyed() {
278 let _ = create_root(|| {
279 let a = create_signal(vec![1, 2, 3]);
280 let mapped = map_keyed(a, |x| x * 2, |x| *x);
281 assert_eq!(mapped.get_clone(), vec![2, 4, 6]);
282
283 a.set(vec![1, 2, 3, 4]);
284 assert_eq!(mapped.get_clone(), vec![2, 4, 6, 8]);
285
286 a.set(vec![2, 2, 3, 4]);
287 assert_eq!(mapped.get_clone(), vec![4, 4, 6, 8]);
288 });
289 }
290
291 #[test]
292 fn keyed_recompute_everything() {
293 let _ = create_root(|| {
294 let a = create_signal(vec![1, 2, 3]);
295 let mapped = map_keyed(a, |x| x * 2, |x| *x);
296 assert_eq!(mapped.get_clone(), vec![2, 4, 6]);
297
298 a.set(vec![4, 5, 6]);
299 assert_eq!(mapped.get_clone(), vec![8, 10, 12]);
300 });
301 }
302
303 #[test]
305 fn keyed_clear() {
306 let _ = create_root(|| {
307 let a = create_signal(vec![1, 2, 3]);
308 let mapped = map_keyed(a, |x| x * 2, |x| *x);
309
310 a.set(Vec::new());
311 assert_eq!(mapped.get_clone(), Vec::<i32>::new());
312 });
313 }
314
315 #[test]
317 fn keyed_use_previous_computation() {
318 let _ = create_root(|| {
319 let a = create_signal(vec![1, 2, 3]);
320 let counter = Rc::new(Cell::new(0));
321 let mapped = map_keyed(
322 a,
323 {
324 let counter = Rc::clone(&counter);
325 move |_| {
326 counter.set(counter.get() + 1);
327 counter.get()
328 }
329 },
330 |x| *x,
331 );
332 assert_eq!(mapped.get_clone(), vec![1, 2, 3]);
333
334 a.set(vec![1, 2]);
335 assert_eq!(mapped.get_clone(), vec![1, 2]);
336
337 a.set(vec![1, 2, 4]);
338 assert_eq!(mapped.get_clone(), vec![1, 2, 4]);
339
340 a.set(vec![1, 2, 3, 4]);
341 assert_eq!(mapped.get_clone(), vec![1, 2, 5, 4]);
342 });
343 }
344
345 #[test]
346 fn keyed_call_cleanup_on_remove() {
347 let _ = create_root(|| {
348 let a = create_signal(vec![1, 2, 3]);
349 let counter = Rc::new(Cell::new(0));
350 let _mapped = map_keyed(
351 a,
352 {
353 let counter = Rc::clone(&counter);
354 move |_| {
355 let counter = Rc::clone(&counter);
356 on_cleanup(move || {
357 counter.set(counter.get() + 1);
358 });
359 }
360 },
361 |x| *x,
362 );
363 assert_eq!(counter.get(), 0, "no cleanup yet");
364
365 a.set(vec![1, 2]);
366 assert_eq!(counter.get(), 1);
367
368 a.set(vec![1, 2, 3]);
369 assert_eq!(counter.get(), 1);
370
371 a.set(vec![1, 3]);
372 assert_eq!(counter.get(), 2);
373 });
374 }
375
376 #[test]
377 fn keyed_call_cleanup_on_remove_all() {
378 let _ = create_root(|| {
379 let a = create_signal(vec![1, 2, 3]);
380 let counter = Rc::new(Cell::new(0));
381 let _mapped = map_keyed(
382 a,
383 {
384 let counter = Rc::clone(&counter);
385 move |_| {
386 let counter = Rc::clone(&counter);
387 on_cleanup(move || {
388 counter.set(counter.get() + 1);
389 })
390 }
391 },
392 |x| *x,
393 );
394 assert_eq!(counter.get(), 0, "no cleanup yet");
395
396 a.set(vec![]);
397 assert_eq!(counter.get(), 3);
398 });
399 }
400
401 #[test]
402 fn indexed() {
403 let _ = create_root(|| {
404 let a = create_signal(vec![1, 2, 3]);
405 let mapped = map_indexed(a, |x| x * 2);
406 assert_eq!(mapped.get_clone(), vec![2, 4, 6]);
407
408 a.set(vec![1, 2, 3, 4]);
409 assert_eq!(mapped.get_clone(), vec![2, 4, 6, 8]);
410
411 a.set(vec![2, 2, 3, 4]);
412 assert_eq!(mapped.get_clone(), vec![4, 4, 6, 8]);
413 });
414 }
415
416 #[test]
418 fn indexed_clear() {
419 let _ = create_root(|| {
420 let a = create_signal(vec![1, 2, 3]);
421 let mapped = map_indexed(a, |x| x * 2);
422
423 a.set(Vec::new());
424 assert_eq!(mapped.get_clone(), Vec::<i32>::new());
425 });
426 }
427
428 #[test]
430 fn indexed_react() {
431 let _ = create_root(|| {
432 let a = create_signal(vec![1, 2, 3]);
433 let mapped = map_indexed(a, |x| x * 2);
434
435 let counter = create_signal(0);
436 create_effect(move || {
437 counter.set(counter.get_untracked() + 1);
438 mapped.track();
439 });
440
441 assert_eq!(counter.get(), 1);
442 a.set(vec![1, 2, 3, 4]);
443 assert_eq!(counter.get(), 2);
444 });
445 }
446
447 #[test]
449 fn indexed_use_previous_computation() {
450 let _ = create_root(|| {
451 let a = create_signal(vec![1, 2, 3]);
452 let counter = Rc::new(Cell::new(0));
453 let mapped = map_indexed(a, {
454 let counter = Rc::clone(&counter);
455 move |_| {
456 counter.set(counter.get() + 1);
457 counter.get()
458 }
459 });
460 assert_eq!(mapped.get_clone(), vec![1, 2, 3]);
461
462 a.set(vec![1, 2]);
463 assert_eq!(mapped.get_clone(), vec![1, 2]);
464
465 a.set(vec![1, 2, 4]);
466 assert_eq!(mapped.get_clone(), vec![1, 2, 4]);
467
468 a.set(vec![1, 3, 4]);
469 assert_eq!(mapped.get_clone(), vec![1, 5, 4]);
470 });
471 }
472
473 #[test]
474 fn indexed_call_cleanup_on_remove() {
475 let _ = create_root(|| {
476 let a = create_signal(vec![1, 2, 3]);
477 let counter = Rc::new(Cell::new(0));
478 let _mapped = map_indexed(a, {
479 let counter = Rc::clone(&counter);
480 move |_| {
481 let counter = Rc::clone(&counter);
482 on_cleanup(move || {
483 counter.set(counter.get() + 1);
484 });
485 }
486 });
487 assert_eq!(counter.get(), 0, "no cleanup yet");
488
489 a.set(vec![1, 2]);
490 assert_eq!(counter.get(), 1);
491
492 a.set(vec![1, 2, 3]);
493 assert_eq!(counter.get(), 1);
494
495 a.set(vec![1, 3]);
496 assert_eq!(counter.get(), 3);
497 });
498 }
499
500 #[test]
501 fn indexed_call_cleanup_on_remove_all() {
502 let _ = create_root(|| {
503 let a = create_signal(vec![1, 2, 3]);
504 let counter = Rc::new(Cell::new(0));
505 let _mapped = map_indexed(a, {
506 let counter = Rc::clone(&counter);
507 move |_| {
508 let counter = Rc::clone(&counter);
509 on_cleanup(move || {
510 counter.set(counter.get() + 1);
511 })
512 }
513 });
514 assert_eq!(counter.get(), 0, "no cleanup yet");
515
516 a.set(vec![]);
517 assert_eq!(counter.get(), 3);
518 });
519 }
520}