Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update an array after splice in Svelte?

I'm learning Svelte, and read in the documentation that arrays need to be reassigned in order for a component or page to update it. For that they devised a more idiomatic solution. Instead of writing:

messages.push('hello');
messages = messages;

you can write instead:

messages = [...messages, 'hello'];

Alright, makes sense. But then the documentation says:

You can use similar patterns to replace pop, shift, unshift and splice.

But how? I cannot see how you can remove items from an array. More to the point, how could I write the following more idiomatically?

messages.splice(messages.indexOf('hello'), 1);
messages = messages;
like image 633
Mielipuoli Avatar asked Nov 20 '19 22:11

Mielipuoli


4 Answers

You could e.g. use the filter array method to create a new array without the element 'hello':

messages = messages.filter(m => m !== 'hello');
like image 133
Tholle Avatar answered Oct 23 '22 01:10

Tholle


As mentioned, Svelte's reactivity is triggered by assignments. The current Svelte tutorial uses JavaScript's (ES6) spread syntax (three dots) to add the next-higher number to an array, providing a more idiomatic solution than a redundant assignment using push:

function pushNumber() {     
    numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}

You could use spread syntax to replace popshiftunshift and splicethough it might increase the time and complexity of the operation in some cases:

function unshiftNumber() {  
    numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4
}

function popNumber() {
    numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3
}

function shiftNumber() {
    numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4
}

function spliceNumber() {
    numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)];// 0, 1, 2, 3
}   

Spread is just one way to do it, though. The purpose behind not using pop/push etc is to encourage immutability. So any removal can just be a filter, for example.

like image 15
Michael McGinnis Avatar answered Oct 23 '22 02:10

Michael McGinnis


There are several things to consider here. Given this code:

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

What's happening here is:

  1. Looking for the first occurrence of the string "hello" in the array
  2. Removing such element from the array, based on the index found.

The assumption here is that "hello" needs to exists, otherwise the could would remove the last item from the array (since indexOf returns -1).

The original array is therefore mutate: depends by the context, that sometimes can be preferable instead of copying the whole array into a new one; otherwise it's generally a better practice avoid such mutation.

So. If you want to have this behavior exactly, probably this is the best code you can have. For example, takes the filter example:

messages = messages.filter(message => message !== "hello")

What's happening here is:

  1. Filter out any element equals to "hello"
  2. Returns a new array without such element

So it's quite different from the original code: first of all, it always loop the whole array. If you have thousands of element, even if you have only one "hello" at the second index, it would always iterate all of them. Maybe it's what you want, maybe not. If the element is unique, such as an id, maybe you want to stop once you find it. Second, it returns a new array. Again, that usually a better practice than mutate the array, but in some context it's preferable mutate it instead of create a new one.

So, if you want to mutate the original array, it's probably better to stick to your original code.

If, instead, you don't care (such as the example of push), I believe that in the intention of svelte's developers, your code would be roughly translate to:

let i = messages.indexOf("hello"); 
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];

(Still assuming there is a "hello" message and you're interested only in the first occurrence).

It's unfortunate that JS doesn't have a better syntax to handles slices.

like image 6
ZER0 Avatar answered Oct 23 '22 01:10

ZER0


In case you're wandering, filter can also be used to remove elements using a given index:

let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']
like image 3
Gabriel F. Engel Avatar answered Oct 23 '22 01:10

Gabriel F. Engel