Array#splice, ce couteau suisse
Par Delicious Insights • Publié le 7 mai 2020

Cet article est également disponible en anglais.

Voici le quatrième article de notre série quotidienne « 19 pépites de JS pur ». Aujourd’hui on se penche sur une méthode qui est là depuis toujours ou presque (JS 1.2, 1997) : Array#splice(…), le couteau suisse du triturage performant de tableau.

Dans la série…

Extrait de la liste des articles :

  1. Extraire efficacement une sous-chaîne de texte
  2. Formater proprement un nombre
  3. Array#splice (cet article)
  4. Strings et Unicode
  5. Court-circuiter plusieurs niveaux de boucles
  6. …au-delà, c’est la surprise ! (mais la liste est déjà calée)…

splice ou slice ?!

Ne pas confondre.

  • slice([from[, until]]) est une opération idempotente / non-mutative, qui ne touche pas au tableau d’origine et renvoie un nouveau tableau basé sur l’intervalle décrit.
  • splice(from[, count[, item…]]) est une opération mutative, qui altère le tableau d’origine en « remplaçant » un segment donné par un nouveau. Comme un épissage ARN, quelque part (et de fait, le terme anglais splicing désigne l’épissage, qu’on parle de gènes, de pellicules de film ou de câbles électriques). On coupe-colle, quoi. C’est le CRISPR des tableaux JavaScript.

Remarque que si slice existe aussi, à l'identique, sur le type String, ce n’est pas le cas de splice, puisqu’une String est par définition immuable.

Pour supprimer

Le « véritable » nom du 2e argument est deleteCount. Ça annonce la couleur : attention chérie, ça va splicer. Si on se limite à deux arguments (voire un), splice supprime du contenu, à partir de la position from (1er argument), lequel autorise les valeurs négatives (comme dans slice).

  • Avec un 2e argument positif, ça vire le nombre d’éléments indiqué (ou jusqu’au bout si le tableau n’est pas assez long).
  • Sans 2e argument, ça vire jusqu’à la fin du tableau.

En tout cas, splice renvoie le segment supprimé, toujours sous forme d’un tableau, qu’il y ait zéro, un ou plusieurs éléments dedans.

const awesomeDevs = ['Aurélie', 'Dan', 'Marie', 'Michel', 'Sara']

awesomeDevs.splice(2, 2) // => ['Marie', 'Michel']
awesomeDevs // => ['Aurélie', 'Dan', 'Sara']
awesomeDevs.splice(-2) // => ['Dan', 'Sara']
awesomeDevs // => ['Aurélie']

Pour remplacer

Il est possible de préciser, sous forme d’arguments individuels, des éléments à insérer en remplacement de ceux supprimés. Ce qui est intéressant, c’est que leur nombre est libre : pas besoin qu’il soit identique à la taille du segment remplacé !

const yummies = ['basilic', 'ciboulette', 'ail', 'oignon', 'échalotte']

yummies.splice(-3, 2, 'cébette') // => ['ail', 'oignon']
yummies // => ['basilic', 'ciboulette', 'cébette', 'échalotte']

yummies.splice(0, 3, 'ail', 'oignon')
// => ['basilic', 'ciboulette', 'cébette']
yummies // => ['ail', 'oignon', 'échalotte']

Et si les éléments de remplacement sont fournis comme un tableau ? Pas de souci, on a le spread, comme toujours :

const yummies = ['basilic', 'ciboulette', 'cébette', 'échalotte']
const lovelyBulbs = ['ail', 'oignon']

yummies.splice(0, 3, ...lovelyBulbs)
yummies // => // => ['ail', 'oignon', 'échalotte']

Pour insérer

Si tu as bien suivi, tu as peut-être réalisé qu’en fait le remplacement est le cas général :

  • pour supprimer, il suffit de ne pas fournir de remplaçants
  • pour insérer, il suffit de ne pas retirer de segment

Eh oui, si count est à zéro mais qu’on fournit des éléments de « remplacement », forcément, on insère.

const coolLanguages = ['javascript', 'rust']
coolLanguages.splice(0, 0, 'elixir', 'haskell') // => []
coolLanguages.splice(-1, 0, 'reason', 'ruby') // => []

coolLanguages
// => ['elixir', 'haskell', 'javascript', 'reason', 'ruby', 'rust']

Pas sur les tableaux typés…

Tu es jeune et bigarré·e et tu fais des tableaux typés ? Ah, ça va poser souci, parce que ceux-ci ont une taille fixe ! Du coup splice est l’une des rares méthodes de Array qui n’y est pas disponible.

…mais quand même sur les pseudo-tableaux

Comme toutes les méthodes de Array, splice n’exige pas d’être appelée sur un « véritable » tableau : il suffit que son sujet soit un « pseudo-tableau » (array-like), c’est-à-dire propose une propriété length numérique (entière positive ou nulle) et des propriétés numériques entre 0 et length - 1.

Bon, tu devrais pas trop en avoir besoin là comme ça, mais c’est quand même rigolo :

const blob = {
  0: 'ça',
  1: 'fait',
  2: 'peur',
  length: 3,
}

// Allez, on appelle `Array#splice` sur `blob`, avec 4 arguments
Array.prototype.splice.call(blob, -2, 1, 'ferait', 'presque')
// => ['fait']
blob
// => { 0: 'ça', 1: 'ferait', 2: 'presque', 3: 'peur', length: 4 }

Copain bonus : copyWithin

Un cas de triturage assez courant consiste à vouloir recopier une partie d’un tableau ailleurs… dans le même tableau. Copier-coller interne, quoi.

On peut bien sûr faire ça avec splice, mais ça suppose un slice en prime :

const lyrics = ['ra', 'ah', 'oh', 'ah', 'roma', 'oh', 'roma', 'ma']
lyrics.splice(1, 2, ...lyrics.slice(0, 2))
lyrics.splice(4, 2, ...lyrics.slice(3, 5))
lyrics // => [ 'ra', 'ra', 'ah', 'ah', 'ah', 'roma', 'roma', 'ma' ]

Mais bon, c’est pas beau, hein… Depuis ES2015, on a copyWithin pour ce genre de cas. La signature est copyWithin(to[, from[, until]]). Attention, to doit être au sein du tableau (inférieur à length), sinon rien ne se passe.

const lyrics = ['ra', 'ah', 'oh', 'ah', 'roma', 'oh', 'roma', 'ma']
lyrics.copyWithin(1, 0, 2)
lyrics.copyWithin(4, 3, 5)
lyrics // => [ 'ra', 'ra', 'ah', 'ah', 'ah', 'roma', 'roma', 'ma' ]

Pour ce type de besoin, c’est imbattable en performance, alors voilà, c’est cadeau.

Back to the future

Au fait, je te parlais déjà de slice et splice en 2014 😉

Envie d’en savoir plus ?

Nos formations envoient du gros pâté, en présentiel ou à distance (FOAD), en inter-entreprises ou en intra rien que pour ta boîte ! Qui plus est, pendant la crise du Covid-19, les salarié·e·s peuvent être formé·e·s gratuitement ! Ce serait vraiment trop bête de ne pas en profiter !

Découvrez notre cours vidéo : JavaScript : this is it ! 🖥

Tout savoir sur le fonctionnement de this en JavaScript, des règles fondamentales aux ajustements des API, en passant par les fonctions fléchées, le binding et bien plus encore…