Array#splice, ce couteau suisse
Par Christophe Porteneuve • Publié le 7 mai 2020
• 4 min
This page is also available in English.
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 :
- Extraire efficacement une sous-chaîne de texte
- Formater proprement un nombre
Array#splice
(cet article)- Strings et Unicode
- Court-circuiter plusieurs niveaux de boucles
- …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 !