Digamos que tengo una matriz var arr = [1, 2, 3], y quiero separar cada elemento por un elemento, por ejemplo. var sep = "&", por lo que la salida es [1, "&", 2, "&", 3].

Otra forma de pensarlo es que quiero hacer Array.prototype.join (arr.join(sep)) sin que el resultado sea una cadena (porque los elementos y el separador que estoy tratando de usar son Objetos, no cadenas).

¿Hay una manera funcional / agradable / elegante de hacer esto en es6 / 7 o lodash sin algo que se siente torpe como:

_.flatten(arr.map((el, i) => [el, i < arr.length-1 ? sep : null])) // too complex

O

_.flatten(arr.map(el => [el, sep]).slice(0,-1) // extra sep added, memory wasted

O incluso

arr.reduce((prev,curr) => { prev.push(curr, sep); return prev; }, []).slice(0,-1)
// probably the best out of the three, but I have to do a map already
// and I still have the same problem as the previous two - either
// inline ternary or slice

Editar: Haskell tiene esta función, llamada intersperse

21
Aaron_H 10 may. 2016 a las 06:11

13 respuestas

La mejor respuesta

Usando un generador:

function *intersperse(a, delim) {
  let first = true;
  for (const x of a) {
    if (!first) yield delim;
    first = false;
    yield x;
  }
}

console.log([...intersperse(array, '&')]);

Gracias a @Bergi por señalar la útil generalización de que la entrada podría ser iterable.

Si no te gusta usar generadores, entonces

[].concat(...a.map(e => ['&', e])).slice(1)
21
10 may. 2016 a las 14:43

Unos años más tarde, aquí hay una solución generadora recursiva . ¡Disfrutar!

const intersperse = function *([first, ...rest], delim){
    yield first;
    if(!rest.length){
      return;
    }
    yield delim;
    yield * intersperse(rest, delim);
};
console.log([...intersperse(array, '&')]);
0
John Henry 16 feb. 2019 a las 05:36

Usando reduce pero sin slice

var arr = ['a','b','c','d'];
var lastIndex = arr.length-1;
arr.reduce((res,x,index)=>{
   res.push(x);
   if(lastIndex !== index)
    res.push('&');
  return res;
},[]);
2
JagsSparrow 10 may. 2016 a las 17:09
const arr = [1, 2, 3];

function intersperse(items, separator) {
  const result = items.reduce(
    (res, el) => [...res, el, separator], []);
  result.pop();
  return result;
}

console.log(intersperse(arr, '&'));
0
Yangshun Tay 26 ene. 2019 a las 20:11

En ES6, escribiría una función generadora que puede producir un iterador que produzca la entrada con los elementos intercalados:

function* intersperse(iterable, separator) {
    const iterator = iterable[Symbol.iterator]();
    const first = iterator.next();
    if (first.done) return;
    else yield first.value;
    for (const value of iterator) {
        yield separator;
        yield value;
    }
}

console.log(Array.from(intersperse([1, 2, 3], "&")));
6
Bergi 10 may. 2016 a las 06:19

Un enfoque directo podría ser como alimentar la función de reducción con una matriz inicial de tamaño uno menos que el doble de nuestra matriz original, llena con el carácter que se utilizará para intercalar. Luego, mapear los elementos de la matriz original en el índice i a 2 * i en la matriz de destino alimentada inicialmente haría el trabajo perfectamente.

En este enfoque, no veo (m) ninguna operación redundante. Además, dado que no estamos modificando ninguno de los tamaños de matriz después de que están configurados, no esperaría que se ejecuten tareas en segundo plano para la reasignación de memoria, optimización, etc. Otra buena parte es usar los métodos de matriz estándar, ya que verifican todo tipo de desajustes Y qué no.

Esta función devuelve una nueva matriz, en la que los elementos de la matriz invocados se intercalan con el argumento proporcionado.

var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
Array.prototype.intersperse = function(s){
  return this.reduce((p,c,i) => (p[2*i]=c,p), new Array(2*this.length-1).fill(s));
}
document.write("<pre>" + JSON.stringify(arr.intersperse("&")) + "</pre>");
4
Redu 10 may. 2016 a las 13:01

Si tiene Ramda en sus dependencias o si está dispuesto a agregarlo, hay intersperse método allí.

De los documentos:

Crea una nueva lista con el separador interpuesto entre elementos.

Se envía al método intercalado del segundo argumento, si está presente.

R.intersperse('n', ['ba', 'a', 'a']); //=> ['ba', 'n', 'a', 'n', 'a']

O puede consultar la fuente para conocer una de las formas de hacerlo en su base de código. https://github.com/ramda/ramda/blob/ v0.24.1 / src / intersperse.js

1
zubko 7 jul. 2017 a las 15:27

Javascript tiene un método join () y split ()

var arr = ['a','b','c','d'];
arr = arr.join('&');
document.writeln(arr);

La salida debe ser: a & b & c & d

Ahora dividido de nuevo:

arr = arr.split("");

Arr es ahora:

arr = ['a','&','b','&','c','&','d'];
2
Bryan 10 may. 2016 a las 03:23

Un retorno extendido y explícito en la función reductora lo hará más conciso:

const intersperse = (arr, sep) => arr.reduce((a,v)=>[...a,v,sep],[]).slice(0,-1)
// intersperse([1,2,3], 'z')
// [1, "z", 2, "z", 3]
9
Klaster_1 10 may. 2016 a las 04:07
if (!Array.prototype.intersperse) {
  Object.defineProperty(Array.prototype, 'intersperse', {
    value: function(something) {
      if (this === null) {
        throw new TypeError( 'Array.prototype.intersperse ' + 
          'called on null or undefined' );
      }
      var isFunc = (typeof something == 'function')

      return this.concat.apply([], 
        this.map(function(e,i) { 
          return i ? [isFunc ? something(this[i-1]) : something, e] : [e] }.bind(this)))
    }
  });
}
0
Carter Cole 21 sep. 2017 a las 15:26

Mi toma:

const _ = require('lodash');

_.mixin({
    intersperse(array, sep) {
        return _(array)
            .flatMap(x => [x, sep])
            .take(2 * array.length - 1)
            .value();
    },
});

// _.intersperse(["a", "b", "c"], "-")
// > ["a", "-", "b", "-", "c"]
0
tokland 20 oct. 2018 a las 11:05

También puedes usar lo siguiente:

var arr =['a', 'b', 'c', 'd'];
arr.forEach(function(element, index, array){
    array.splice(2*index+1, 0, '&');
});
arr.pop();
0
José De Araujo 2 oct. 2017 a las 15:55

Actualizado para objetos que no usan el método de unión:

for (var i=0;i<arr.length;i++;) {
    newarr.push(arr[i]);
    if(i>0) { 
      newarr.push('&'); 
     }    
}

Newarr debe ser:

newarr = ['a','&','b','&','c','&','d']; 
-2
Bryan 10 may. 2016 a las 18:37