Organize multidimensional array in javascript, preventing the next item from having the same type as the previous one

Asked

Viewed 209 times

1

I have a logic problem here. Let’s see if you can help, because I’m burning neurons here and I haven’t found a starting point.

I have an array of objects and need to organize it in a way that the previous item cannot be equal to the next item. Something like this

var result = [
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" }
];

The way it is, when it is displayed on the screen, it will show 3 blocks in a row text in the array interaction. It would be necessary to make an interaction, to balance the items to the maximum, not to leave two blocks followed by text. Of course the array can come clogged with items of the type text or only with them, not being able to interim duty, but this is not the case now.

From the above item, the result would have to look something like this:

var result = [
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" }
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" }
];

Has anyone been through this? Help will be welcome :)

PS: repeat in the sense that the next item may be a type image or video, but cannot have 2 text followed (unless there is no way to organize by having only text or many texts)

  • Has any response helped solve the problem and can address similar questions from other users? If so, make sure to mark the answer as accepted. To do this just click on the left side of it (below the indicator of up and down votes).

2 answers

1

Taking the solution only from the logical side I would do the following:

  • It would separate the array main organizing by the types in other arrays underage;

  • Check which had the highest number of occurrences;

  • Would travel the arrays smaller until the end or until the quantity observed above, intervening by the keys;

  • I would add in a array final;

The code would look like this:

var result = [
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" },
    {  type: 'image',  name: 'foo image', src: 'http://www.something.com/path/to/image.jpg', text: "lorem ipsum sit amet" },
    {  type: 'text',  name: 'foo text', src: '', text: "lorem ipsum sit amet" },
    {  type: 'video',  name: 'foo video', src: 'http://www.something.com/path/to/video.mp4', text: "lorem ipsum sit amet" }
];

var organizador = {};
var tamanhoMaximo = 0;
var final = [];

result.forEach(function(item) {
  // Garante a criação do tipo no array organizador
  if (!organizador.hasOwnProperty(item.type)) {
    organizador[item.type] = [];
  }

  organizador[item.type].push(item);
});

// Percorre o objeto pra saber o item de maior tamanho
for (var type in organizador) {
  var tamanho = organizador[type].length;
  
  if (tamanho > tamanhoMaximo) {
    tamanhoMaximo = tamanho;
  }
}

// Percorre os arrays
for (var indice = 0; indice < tamanhoMaximo; indice++) {
  // Percorre as chaves
  for (var type in organizador) {
    if (indice < organizador[type].length) { // Verifica se o array tem item neste índice
      final.push(organizador[type][indice]); // Adiciona ao resultado final
    }
  }
}

document.write(JSON.stringify(final, null, 2));

Resulting in:

[
  { "type": "image", "name": "foo image", "src": "http://www.something.com/path/to/image.jpg", "text": "lorem ipsum sit amet" },
  { "type": "video", "name": "foo video", "src": "http://www.something.com/path/to/video.mp4", "text": "lorem ipsum sit amet" },
  { "type": "text", "name": "foo text", "src": "", "text": "lorem ipsum sit amet" },
  { "type": "image", "name": "foo image", "src": "http://www.something.com/path/to/image.jpg", "text": "lorem ipsum sit amet" },
  { "type": "video", "name": "foo video", "src": "http://www.something.com/path/to/video.mp4", "text": "lorem ipsum sit amet" },
  { "type": "text", "name": "foo text", "src": "", "text": "lorem ipsum sit amet" },
  { "type": "video", "name": "foo video", "src": "http://www.something.com/path/to/video.mp4", "text": "lorem ipsum sit amet" },
  { "type": "text", "name": "foo text", "src": "", "text": "lorem ipsum sit amet" },
  { "type": "text", "name": "foo text", "src": "", "text": "lorem ipsum sit amet" }
]

1

You can do this in two steps: organize by keys, and then merge. In the end clean the empty positions with .filter(Boolean);.

The code would be like this:

// agrupar por chaves
var tipos = original.reduce(function(obj, entrada) {
    if (!obj[entrada.type]) obj[entrada.type] = [];
    obj[entrada.type].push(entrada);
    return obj;
}, {});
// ficas com {image: [...], video: [...], text: [...]}

// intercalar
var resultado = Object.keys(tipos).reduce(function(arr, tipo, i, _tipos){
    let entrada, pos = 0 + i, qtd = _tipos.length;
    // posicionar de espaçados por "qtd" de tipos, começando pelo index do tipo em Object.keys(tipos)
    while (entrada = tipos[tipo].shift()){
        arr[pos] = entrada || null;
        pos+= qtd;
    }
    return arr;
}, []).filter(Boolean);

Example: https://jsfiddle.net/4mnxfkra/

Browser other questions tagged

You are not signed in. Login or sign up in order to post.