Groupby in Javascript

Asked

Viewed 1,862 times

6

I’m getting the following result from a query:

[
  {
    "disciplina":"Portugues",
    "periodo":"1º Bimestre",
    "tipo":"1ª avaliacao",
    "valor":9.5
  },
  {
    "disciplina":"Matematica",
    "periodo":"1º Bimestre",
    "tipo":"1ª avaliacao",
    "valor":9.5
  }
]

How best to leave the result grouped by period, then by discipline and then by type?

I’m looking for that result:

{
  "periodo":"1º Bimestre",
  "disciplinas":[
    {
      "nome":"Português",
      "tipo":[
        {
          "nome":"1ª avaliacao",
          "valor":9.5
        },
        {
          "nome":"2ª avaliacao",
          "valor":9.5
        }
      ]
    },
    {
      "nome":"Matemática",
      "tipo":[
        {
          "nome":"1ª avaliacao",
          "valor":8.5
        },
        {
          "nome":"2ª avaliacao",
          "valor":7.5
        }
      ]
    }
  ]
}

2 answers

7


I ended up developing a more generic function, presented below.

function group_by (lista, coluna) {
  var colunas = {};
  var resultado = [];

  lista.forEach(function (item) {
    var reg = {};

    colunas[item[coluna]] = colunas[item[coluna]] || [];

    for (var i in item) 
      if (i != coluna) 
        reg[i] = item[i]; 

    colunas[item[coluna]].push(reg);
  });

  for (var i in colunas) 
    resultado.push({key: i, values: colunas[i]});

  return resultado;
}

She naturally does the group by on only one level, returning a list of objects with the attributes key, with the value of the selected column, and values, with the remaining values.

Consider the entry below:

[
  {
    "disciplina": "Portugues",
    "periodo": "1º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 9.5
  },
  {
    "disciplina": "Matematica",
    "periodo": "1º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 9.5
  },
  {
    "disciplina": "Matematica",
    "periodo": "1º Bimestre",
    "tipo": "Trabalho",
    "valor": 9.5
  },
  {
    "disciplina": "Matematica",
    "periodo": "2º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 6.3
  }
]

Executing the group_by over the column periodo, group_by(lista, "periodo"), we would have the following output:

[
  {
    "key":"1º Bimestre",
    "values":[
      {
        "disciplina":"Portugues",
        "tipo":"1ª avaliacao",
        "valor":9.5
      },
      {
        "disciplina":"Matematica",
        "tipo":"1ª avaliacao",
        "valor":9.5
      },
      {
        "disciplina":"Matematica",
        "tipo":"Trabalho",
        "valor":9.5
      }
    ]
  },
  {
    "key":"2º Bimestre",
    "values":[
      {
        "disciplina":"Matematica",
        "tipo":"1ª avaliacao",
        "valor":6.3
      }
    ]
  }
]

To get a group by second-degree, so to speak, it is sufficient to perform the function again on the present list in values, in the desired column. For example, to do the group by over the column disciplina and get the expected result, we do:

group_by(lista, "periodo").map(function (item) {
  return {key: item.key, values: group_by(item.values, "disciplina")};
});

Producing the following result:

[
  {
    "key":"1º Bimestre",
    "values":[
      {
        "key":"Portugues",
        "values":[
          {
            "tipo":"1ª avaliacao",
            "valor":9.5
          }
        ]
      },
      {
        "key":"Matematica",
        "values":[
          {
            "tipo":"1ª avaliacao",
            "valor":9.5
          },
          {
            "tipo":"Trabalho",
            "valor":9.5
          }
        ]
      }
    ]
  },
  {
    "key":"2º Bimestre",
    "values":[
      {
        "key":"Matematica",
        "values":[
          {
            "tipo":"1ª avaliacao",
            "valor":6.3
          }
        ]
      }
    ]
  }
]

How about testing for a larger input? Let’s assume further that we want to separate the records according to the tipo, to separate notes from proofs, lists, etc.

group_by(lista, "periodo").map(function (item) {
  return {key: item.key, values: group_by(item.values, "disciplina").map(function(item){
    return {key: item.key, values: group_by(item.values, "tipo")}
  })};
})

The way out:

[
  {
    "key":"1º Bimestre",
    "values":[
      {
        "key":"Portugues",
        "values":[
          {
            "key":"Prova",
            "values":[
              {
                "valor":9.5
              }
            ]
          },
          {
            "key":"Lista",
            "values":[
              {
                "valor":8.8
              },
              {
                "valor":5.2
              }
            ]
          }
        ]
      },
      {
        "key":"Matematica",
        "values":[
          {
            "key":"Prova",
            "values":[
              {
                "valor":9.5
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "key":"2º Bimestre",
    "values":[
      {
        "key":"Biologia",
        "values":[
          {
            "key":"Prova",
            "values":[
              {
                "valor":9.5
              }
            ]
          },
          {
            "key":"Lista",
            "values":[
              {
                "valor":10
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "key":"3º Bimestre",
    "values":[
      {
        "key":"Geografia",
        "values":[
          {
            "key":"Lista",
            "values":[
              {
                "valor":9.5
              },
              {
                "valor":9.9
              }
            ]
          }
        ]
      }
    ]
  }
]
  • This solution served perfectly! Excellent! Thank you very much Anderson. Hug.

  • 1

    Perhaps it is still possible to improve it using recursiveness to call something like group_by(lista, ["periodo", "disciplina"]), but it was late and I couldn’t think anymore.

  • Friend, how do I convert the result of this code into a table?

  • 1

    @Italorodrigo just do the for us values and build the table.

6

I do not know if it is the best way, or the most efficient, but it generates the result you need. I believe that with the comments in the code it is possible to understand the logic.

var lista = [
  {
    "disciplina": "Portugues",
    "periodo": "1º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 9.5
  },
  {
    "disciplina": "Matematica",
    "periodo": "1º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 9.5
  },
  {
    "disciplina": "Matematica",
    "periodo": "2º Bimestre",
    "tipo": "1ª avaliacao",
    "valor": 6.3
  }
]

function group_by (lista) {
  
  var resultado = [];
  
  // Percorre todos os registros
  lista.forEach(function (item) {
    
    var idp = -1;
    var idd = -1;
    
    // Verifica se o periodo já está na lista final
    // Se sim, idp será o índice do respectivo objeto
    for (i = 0; i < resultado.length; i++) {
      if (item.periodo == resultado[i].periodo) {
        idp = i;
        break;
      }
    }
    
    // Se não, idp será -1, então cria o objeto na lista
    if (idp == -1) {
      idp = resultado.length;
      resultado.push({"periodo": item.periodo, "disciplinas": []});
    }
    
    // Verifica se a disciplina já está na lista do periodo
    // Se sim, idd será o índice do respectivo objeto
    for (i = 0; i < resultado[idp].disciplinas.length; i++) {
      if (item.disciplina == resultado[idp].disciplinas[i].nome) {
        idd = i;
        break;
      }
    }
    
    // Se não, idd será -1, então cria o objeto na lista
    if (idd == -1) {
      idd = resultado[idp].disciplinas.length;
      resultado[idp].disciplinas.push({"nome": item.disciplina, "tipo": []});
    }
    
    // Insere o tipo/valor na respectiva disciplina e periodo
    resultado[idp].disciplinas[idd].tipo.push({"nome": item.tipo, "valor": item.valor});
    
  });
  
  return resultado;
  
}

$(function () {
  $('pre').html( JSON.stringify(group_by(lista), null, 2) );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre>teste</pre>

Press Execute to see the code working.

  • I think that’s right. I’ll just adapt pro typescript because I’m using it in a job with angular 2. Thank you.

Browser other questions tagged

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