Generate one array according to another without bottlenecks

Asked

Viewed 55 times

3

I have the following object array:

[
   { 
      id: "1", 
      name: "ProdutoA",
      categorias: [
          {idCat: 1, name: "CategoriaA"},
          {idCat: 2, name: "CategoriaB"}
      ] 
   },
   { 
      id: "2", 
      name: "ProdutoB",
      categorias: [
          {idCat: 1, name: "CategoriaA"}
      ] 
   }
]

From this object array, I need to generate another array with the following structure:

[
   {idCat: 1, name: "CategoriaA", products: ["ProdutoA","ProdutoB"]},
   {idCat: 2, name: "CategoriaB", products: ["ProdutoA"]}
]

I don’t know how to do this using Javascript best practices, only using the cycle for, but I don’t know if it will be performatic when the first array is very large.

How to generate the second array according to the first?

Observing: The initial object array will always be consistent and is produced this way through a framework. So, all elements with the same idCat will always have the same name, and therefore it is not necessary to worry about a hypothetical case where two elements with the same idCat have names different.

  • 2

    Could you define too big? for example, an array with 1000 positions may be large for a human to process visually, but for a computer it is small.

  • But since your idea is to create a new array based on the first one, then the ideal is to use Array.prototype.map in place of for.

  • And what should happen if you have {categoris : [ {idCat: 1, name : 'CategoriaA'}]} and another with {categoris : [ {idCat: 1, name : 'CategoriaB'}]}?

  • @Tobymosque imagine that it is 1000 positions, but in "categories" I have 10 records, 10 thousand records processed, the problem is that I have to check the new array every time I want to check if the category already exists.

  • @Guilhermelautert this cannot occur, there are no different categories with the same ID, at least that’s what you used as an example, notice that "Categoriaa" has the same "idCat" as "Categoriab".

  • @Filipemoraes are you agree that name is irrelevant and idCat who should point to the respective category?

  • 1

    @Filipemoraes I think what Guilherme means is that his structure "violates the third normal form" hehe. For if in categorias you already have idCat and nome, Why repeat the same parameters on each product, why not just have a list of Ids in the product? I understand that maybe this data is being generated by a tool, and so cannot be changed, so in this case we can assume that a solution does not need to worry about it?

  • @Guilhermelautert yes, that’s it, but the name has to be included in the new array, but the connection is done via idCat.

  • @mgibsonbr yes, the first array is generated by a framework, the second array is what I need, so the need to transform it.

  • P.S. The underscore.js library has a method groupBy that could help you with this, but the output it generates is another (it would have to be adapted), and anyway it does not pay to include a library only to use a function, especially when seeking efficiency. Better do it by hand anyway...

  • In short you want to go through the whole array verificando quais produtos estão vinculado com cada idCat`

  • @Exact Guilhermelautert, according to the example I posted.

  • @mgibsonbr then, my doubt is how to do this by hand without the code getting heavy, that’s my concern! I can do it using only the FOR cycle.

  • @Filipemoraes I don’t think using the for is bad, especially if your logic is correct. I wrote a response using forEach, but it would be the same (and even more efficient, I think) using fors, what matters is that the number of operations executed - as far as my knowledge goes - is minimal, you could hardly achieve a more efficient solution...

  • 1

    This question is in the closing row as "is not clear enough". To me she seems to be clear, so I’m going to vote on "Leave open". Could someone there explain why they want to shut it down?

  • @Victorstafusa I voted, because in the 3,5,6,7,8 comments that only then can get the result as expected, without considering this, and expected response would not be possible. Then it would be better to point them out in the question.

  • 2

    @Guilhermelautert Ok, I edited the question to try to cover this case based on what is written in the comments.

  • @Guilhermelautert Looking at the example I gave, all questions 3,5,6,7 and 8 are answered. In the example it is possible to see that the connection is by idCat and that the category name is not repeated. The question is clear, how to turn an array A into an array B.

Show 13 more comments

2 answers

3


Since category Ids are simple numbers, an object mapping ID to structure should be sufficient:

function converter(produtos) {
  var map = {};
  
  produtos.forEach(function(produto) {
      produto.categorias.forEach(function(cat) {
          if ( !map[cat.idCat] )
              map[cat.idCat] = { idCat:cat.idCat, name:cat.name, products:[] };
          map[cat.idCat].products.push(produto.name);
      });
  });
  
  var ret = [];
  for ( var p in map )
    ret.push(map[p]);
  // Opcional: ordena o retorno
  return ret;
}

var entrada = [
   { 
      id: "1", 
      name: "ProdutoA",
      categorias: [
          {idCat: 1, name: "CategoriaA"},
          {idCat: 2, name: "CategoriaB"}
      ] 
   },
   { 
      id: "2", 
      name: "ProdutoB",
      categorias: [
          {idCat: 1, name: "CategoriaA"}
      ] 
   }
];
document.body.innerHTML += "<pre>" + JSON.stringify(converter(entrada), null, 4) + "<pre>";

  • 1

    It was faster and even better than I was thinking. + 1

1

Look, I think the best way is to use a little Prototype and create two lists (Products and Category) and create references between them.

var estrutura = [
  { 
    id: "1", 
    name: "ProdutoA",
    categorias: [
      {idCat: 1, name: "CategoriaA"},
      {idCat: 2, name: "CategoriaB"}
    ] 
  },
  { 
    id: "2", 
    name: "ProdutoB",
    categorias: [
      {idCat: 1, name: "CategoriaA"}
    ] 
  }
];

var produtos = {};
var categorias = {};

var Produto = function (item) {
  this.id = item.id;
  this.name = item.name;
  this.categorias = [];
}

var Categoria = function (subitem) {
  this.idCat = subitem.idCat;
  this.name = subitem.name;
  this.produtos = [];
}

estrutura.forEach(function (item, indice) {
  var produto = new Produto(item);
  produtos[item.id] = produto;

  item.categorias.forEach(function (subitem, indice) {
    var categoria = categorias[subitem.idCat];
    if (!categoria) {
      categoria = new Categoria(subitem);
      categorias[subitem.idCat] = categoria;
    }
    categoria.produtos.push(produto);
    produto.categorias.push(categoria);
  });
});

console.log(categorias, produtos);

in this case, you can access the product or category by ID:

var produto = produtos[1]; //retorna o ProdutoA;
var categoria = categoria[1]; //retorna a CategoriaA;

as well as being able to navigate within the properties:

var irmaos = produtos[2].categorias[1].produtos; //todos os produtos da categoriaA

Browser other questions tagged

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