How to reverse the order in svelte {#each ...} block
Asked Answered
G

4

6

Is it possible to reverse the order in which the items are displayed in svelte's {#each ...} block?

I want this for an array of object, sorted by id, where the oldest entry comes first. And I want to display the newest entry first.

Edit: To illustrate what happens when using the .reverse() solution, here the 'before' and 'after' transition screens: before

after

Gastronomy answered 18/12, 2019 at 13:59 Comment(1)
I have an alternative maybe; to first reverse the original array before it is fed to the {#each ..} block, but I thought that it could be easier to do the reversing while it is displayed.Gastronomy
R
9

Looking at the docs I tried:

<script>
    let cats = [
        { id: 'J---aiyznGQ', name: 'Keyboard Cat' },
        { id: 'z_AbfPXTKms', name: 'Maru' },
        { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
    ];
</script>

<h1>The Famous Cats of YouTube</h1>

<ul>
    {#each [...cats].reverse() as { id, name }, i}
        <li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
            {i + 1}: {name}
        </a></li>
    {/each}
</ul>

Seems to work... Try it here

The reason for the [...cats].reverse() is so that we avoid reversing the actual cats array and just reverse a copy

Ryun answered 18/12, 2019 at 14:3 Comment(5)
I works, yes, with the first render of the array. But, looking at the 'transition' example (that I have used as a 'model') - well, after a transition the original order is restored, and that is not what I wanted!Gastronomy
hmm, interesting. Because it reverses the data each time it fades in. Will have a playRyun
{#each [...cats].reverse() as { id, name }, i}Ryun
no, using copy with [... ] does not solve it. Unfortunately.Gastronomy
I feel like if you need to do more work than that then it belongs in the script tags like you said, keeping view and logic separateRyun
C
2
<script>
  // Sorted in A-Z
  const arr = ['Affectionate', 'Candid', 'Synthetic Heart', 'Your Eyes', 'Yours,Truly'];
</script>

<ol>
  {#each { length: arr.length } as _, index}
    {@const reverseIndex = arr.length - 1 - index}
    {@const value = arr[reverseIndex]}
    <!-- Shown in Z-A -->
    <li>{value}</li>
  {/each}
</ol>

<!-- 
 1. Yours,Truly
 2. Your Eyes
 3. Synthetic Heart
 4. Candid
 5. Affectionate
-->

Demo. Instead of creating another array with Array.reverse(), how about using reverse index and its value.

Constellate answered 21/2, 2023 at 4:35 Comment(1)
The {#each ...} was weird to me at first. Now does it mean that the each block only cares about whether there's a length property on the object it iterates over and tries index accesses ? That is an object doesn't even have to be iterable ? Because for (el in {length: 5}) {} will only iterate once and for (el in {length: 5}) {} gives an errorStrobel
B
1

the svelte 5 easier way

<script>
    let cats = [
        { id: 'J---aiyznGQ', name: 'Keyboard Cat' },
        { id: 'z_AbfPXTKms', name: 'Maru' },
        { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' },
    ]
    function* items() {
        for (let i = cats.length - 1; i >= 0; i--) {
            yield cats[i]
        }
    }
</script>

<h1>The Famous Cats of YouTube</h1>

<ul>
    {#each items() as { id, name }, id}
        <li>
            <a target="_blank" href="https://www.youtube.com/watch?v={id}">
                {i + 1}: {name}
            </a>
        </li>
    {/each}
</ul>
Borras answered 12/5 at 17:15 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Newland
G
0

The answer is that it is difficult, maybe impossible, to implement a solution by tweeking in the {#each}. Like dbramwell suggested, I do the work in the script. The code might need some improvement/refactoring!

function reverseTodos() {
    let indexArr = [];
    for (const todo of todos) {
      indexArr.push(todo.id);
    }
    indexArr.reverse();

    let myNewArr = [];
    for (let i = 0; i < todos.length; i++) {
      myNewArr[i] = {
        id: indexArr[i],
        todo: todos[i].todo,
        checkbox: todos[i].checkbox
      };
    }
    return myNewArr.reverse();
  }

  $: reversedTodos = reverseTodos();

And then I feed reversedTodos to the {#each} block.

Gastronomy answered 18/12, 2019 at 17:3 Comment(2)
I just found out that this proposed solutions isn't the best! Because now, after changeing the ids of the elements, it is impossible to delete (and likely also to edit) the individual elements. I think that the original ids should be maintained, and the new ids added to the reversedTodos. Then I can index the elements by using the 'new ids', while keeping the original ids!Gastronomy
And yet there is another issue... I changed the code as I described before; I can see in the console that items are deleted, but now the updated array is not displayed on the screen as is.Gastronomy

© 2022 - 2024 — McMap. All rights reserved.