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

1

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

I have an array of objects and I need to organize them in a way that the previous item can not be the same as 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, will show 3 consecutive blocks of type 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 be clogged with items like text or just with them, not being able to merge right, but that's 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 ever been in this situation? All help will be welcome :)

PS: Repeat in the sense that the next item can be a type image or video , but can not have 2 text followed (save situation where you can not organize because only text or many texts) / p>     

asked by anonymous 23.11.2016 / 03:56

2 answers

1

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

  • It would separate the array principal arranging by the types in other arrays smaller;

  • It would check which had the highest number of occurrences;

  • I would go through the% w / w of the lowest% up to the end or up to the amount observed earlier, by spanning the braces;

  • I would add in a final arrays

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" }
]
    
23.11.2016 / 04:51
1

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

The code would look 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: link

    
23.11.2016 / 10:19