[Resolvido] Merge dois objetos por ID em JSON


(Matheus Verissimo) #1

Fala galera, gostaria de saber como eu faço para relacionar dois JSON distintos.

Por exemplo dar um get em todos os users e suas respectivas tarefas, por enquanto eu só consigo pegar via passagem de parâmetro, a ideia seria listar todos automático.

/* user data */
/people/123.json
{
people: [{
    last-name: "Ipsum",
    email-address: "loremipsum@gmail.com",
    user-name: "Lorem Ipsum",
    company-name: "Dollor",
    id: "123",

  }

}


/* task data */
/projects/projectID/tasklists.json?responsible-party-id=userID
{
  STATUS: "OK",
    tasklists: [{
      id: "505264",
      complete: false,
      projectId: "123",
      projectName: "Lorem Ipsum"
  }
}

(Douglas Oliveira Garrido) #2

Acredito que o caminho é você melhorar sua pesquisa no banco de dados, não sei qual banco você utiliza, mas seria o equivalente ao JOIN, assim sua pesquisa juntaria USUÁRIOS com TAREFAS e retornaria somente um JSON. Dessa forma você realiza um request apenas e não dois.

Caso precise realmente retornar o valor em dois JSON's, recomendo um script muito bom que utiliza o LINQ (Sim, o mesmo do C#), só que na versão JavaScript. Segue o link: https://linqjs.codeplex.com/.

Ex:

Enumerable.From(tasksJson).Where(function (x) {
  return x.projectId == userID;
}).Select(function (x) {
  return x;
}).ToArray();

Essa é uma forma de pegar todas as tasks de um usuário específico, porém tem muitas outras funcionalidades o linq.js, de uma olhada na documentação dele.

Espero ter ajudado.


(Matheus Verissimo) #3

O Problema é que estou consumindo de um webservice(api) esses dados


(Diego Eis) #4

(Douglas Oliveira Garrido) #5

@mverissimo, eu fiz um pequeno teste utilizando a biblioteca Javascript que eu havia mencionado anteriormente (linq.js). Veja se é isso que está precisando, segue o Fiddle abaixo:

https://jsfiddle.net/DouglasGarrido/anvz6ycn/embedded/result/

Qualquer dúvida, me pergunte.


(Matheus Verissimo) #6

Quase isso!
Todas tarefas possui um id da pessoa responsável, o que deve ser feito é comparar o id atual com o id do responsável:
user['id'] == task['responsible-party-id'] com isso trazer todas tarefas associadas a esse user.

A minha forma de fazer foi a seguinte:

http://pastebin.com/tzer225c

O ideal seria, conforme fosse dando true na comparação ir adicionando a um vetor, algo parecido com isso:
http://pastebin.com/ZNVCG1Gp


(Douglas Oliveira Garrido) #7

@mverissimo, com a ajuda de um colega meu (@marioaleogolsat) mais experiente no Angular, encontramos algumas opções:

  1. (RECOMENDADO):
    Juntar os dois JSON's como mencionei anteriormente, aonde se tem um Array de Usuários, sendo que cada Usuário tem um Array de Tarefas. Assim tendo um JSON apenas. Supondo que você tenha aquele JSON único, que está no exemplo que eu mostrei, ficaria assim no HTML:

    <div ng-repeat="user in Users">
       <div class="panel panel-default">
          <div class="panel-heading"> {{ user['user-name'] }} </div>
          
          <table class="table table-striped">
                <thead>
                   <tr>
                      <th> Task </th>
                   </tr>
                </thead>
                <tbody>
                   <tr ng-repeat="task in user.tasks">
                      <td>
                         <a href="{{ task.id }}", target="_blank"> {{ task.projectName }} </a>
                     </td>
                  </tr>
                </tbody>
          </table>
       </div>
    </div>

Veja, dessa forma você evita diversas iterações com o DOM, ganhando performance. Processando tudo por trás e entregando o "produto" pronto para o HTML só mostrar na tela.

  1. Caso queira que o Angular processe cada Task baseando-se no Usuário, conforme o exemplo que você falou, uma dica é tentar usar o "ng-if".

  2. Caso queira que o Angular processe cada Task baseando-se no Usuário, conforme o exemplo que você falou, uma outra dica é usar "directive", assim você tem o controle dos dados, e poderá então "montar" o HTML do seu gosto.


Na minha opinião, recomendo utilizar a primeira opção, assim evitando diversas iterações com o DOM. Evite processar os dados no front da aplicação, faça o que puder antes, desde cálculos, até mesmo minerando os dados e entregando a informação "polida" para a tela (HTML).

Veja ai se algo resolve. Desculpe qualquer coisa. Qualquer dúvida, só perguntar.


(Matheus Verissimo) #8

Show a primeira opção funcionou perfeitamente, o único problema agora é para apresentar os dados(ng-repeat), quando eu faço isso:

<div ng-repeat="user in users">
   <div class="panel panel-default">
      <div class="panel-heading"> {{ user['user-name'] }} </div>
      
      <table class="table table-striped">
            <thead>
               <tr>
                  <th> Task </th>
               </tr>
            </thead>
            <tbody>
               <tr ng-repeat="task in user.tasks">
                  <td>
                     <a href="{{ task.id }}", target="_blank"> {{ task.projectName }} </a>
                 </td>
              </tr>
            </tbody>
      </table>
   </div>
</div>

Ele não apresenta nada, já quando faço isso:

ng-repeat="user in users[13].user, ng-repeat="user[13].tasks, ele me trás um usuário;

json return:


Segue meu code: http://pastebin.com/cwdcQVas

Muito obrigado mesmo pela ajuda até agora.


(Douglas Oliveira Garrido) #9

@mverissimo, tente mudar o seguinte no HTML:

De:

<div class="panel-heading"> {{ user['user-name'] }} </div>

Para:

<div class="panel-heading"> {{ user.people['user-name'] }} </div>

Veja se resolve. Caso não resolva, debugue o código pelo Chrome Console, coloque breakpoints dentro dos "return" da função Linq. Caso não consiga (se o arquivo JS estiver minificado), coloque alguns "console.log" para ver os valores que passam nos "return".

Qualquer dúvida, só perguntar.


(Matheus Verissimo) #10

Eu testei da seguinte form e funcionou:

ng-repeat="user in users"
{{ user['people']['first-name'] }}

porém para as tasks isso não vai:
ng-repeat="user in users"
{{ user['tasks']['id'] }}


(Douglas Oliveira Garrido) #11

Nas tasks, observe que elas estão dentro do array "user" e as tasks é um array de objetos, teste assim:

<tr ng-repeat="task in user['tasks']">
   <td>
      <a href="{{ task['id'] }}", target="_blank"> {{ task['projectName'] }} </a>
   </td>
</tr>

(Matheus Verissimo) #12

Entendido erro meu mesmo me desculpe, mas algumas coisas não estão claras para mim como:

Quando eu faço:

 <tr ng-repeat="user in uses['people']">
    <td>
       <span>{{ user['first-name'] }}</span>
    </td>
 </tr>

Ele não me retorna nada, agora se eu faço isso:

<tr ng-repeat="user in uses">
    <td>
       <span>{{ user['people']['first-name'] }}</span>
    </td>
</tr>

Funciona, pq?

Quanto as tarefas, se eu fizer isso:

<tr ng-repeat="task in user['tasks']">
  <td>
    <a href="{{ task['id'] }}", target="_blank"> {{ task['projectName'] }} </a>
  </td>
</tr>

Funciona perfeitamente, mas se eu fizer isso:

<tr ng-repeat="task in users['tasks']">
  <td>
     <a href="{{ task['id'] }}", target="_blank"> {{ task['projectName'] }} </a>
  </td>
</tr>

Nota que eu passo que quando eu passo user e ele funciona e users já não.
Ele estaria pega a referencia do primeiro ng-repeat="user in users"?

Muito obrigado mesmo pela ajuda até agora.


(Douglas Oliveira Garrido) #13

Não precisa pedir desculpas não, hehehe. Estamos aqui para aprender, fique tranquilo.

Vamos lá, em relação as dúvidas:

  1. Dessa forma ng-repeat="user in users['people']" você não estaria fazendo o que você havia mencionado, que é mostrar o usuário e as tarefas relacionadas a ele, você estaria pegando apenas o primeiro Objeto people e nada mais, só que também não daria certo, já que user['people'] não é um Array de objetos, portanto dessa forma você não conseguia realizar a iteração no Array de Usuários, por isso tem que ser ng-repeat="user in users". Ou seja, um for em cada Usuário.

  2. Dessa forma ng-repeat="task in users['tasks']" você não conseguiria pegar porque está dentro do contexto do primeiro "ng-repeat", conforme você mencionou. Para que funcione assim ng-repeat="task in users['tasks']", você teria que passar o index do users antes de tasks, exemplo: users[0]['tasks'], depois users[1]['tasks'] e assim por diante. Por isso você faz um ng-repeat aninhado, como se fosse um for dentro de outro for.

  3. Uma dica é ver todo o contexto e o objetivo que você quer alcançar, que seria mostrar uma "lista" de Usuários e dentro de cada Usuário, suas Tarefas relacionadas.

Espero ter ajudado. Qualquer dúvida, pode perguntar, não sou muito experiente, mas tento ajudar no que for preciso, hehehe.


(Matheus Verissimo) #14

Entendi, funcionou muito obrigado pela ajuda, uma última dúvida quanto o linqjs estou tentando trazer apenas os usuários no quais realmente possuem tarefa, pq no caso ele traz todos os usuários independente se possuem tarefa ou não, tentei de várias formas mas sem sucesso. Eu teria que fazer outro select ali dentro e retorna os usuários?

Meu code:

function success(obj) {
$scope.dataUsers = obj[0].data['people'];
$scope.dataTasks = obj[1].data['todo-items'];

  self = this;
  $scope.users = $linq.Enumerable().From($scope.dataUsers).Select(function(x, y) {
    self.tasksList = $linq.Enumerable().From($scope.dataTasks).Where(function(y) {
      return x['id'] == y['responsible-party-id'];
    }).Select(function(y) {
      return y;
    }).ToArray();

    if (this.tasksList != '') {
      jsonUser = {
        'people': x,
        'tasks': tasksList
      };
      return jsonUser;
    }

  }).ToArray();

}

(Douglas Oliveira Garrido) #15

Ficaria assim:

$scope.users = $linq.Enumerable().From($scope.dataUsers).Where(function (x) {
      // Filtra somente usuários que possuem tarefas
      var tarefa = $linq.Enumerable().From($scope.dataTasks).Where(function (y) {
        return x['id'] == y['responsible-party-id'];
      }).Select(function (y) {
       	return y;
      }).ToArray();
    
      return tarefa.length > 0;
    }).Select(function(x, y) {
    self.tasksList = $linq.Enumerable().From($scope.dataTasks).Where(function(y) {
      return x['id'] == y['responsible-party-id'];
    }).Select(function(y) {
      return y;
    }).ToArray();

    if (this.tasksList != '') {
      jsonUser = {
        'people': x,
        'tasks': tasksList
      };
      return jsonUser;
    }

  }).ToArray();

Você coloca a cláusula Where no seu primeiro Select que é dos Usuários, ou seja, só traz Usuários que possuem Tarefas (Código: Só traz os Usuários se o Array de Tarefas dele for maior que 0).

Uma outra dica, dá uma olhada nesse site http://neue.cc/reference.htm, tem todos as referências do LinqJS, como também exemplos e no início um Editor para testar as "lógicas", rsrs.

Qualquer dúvida, só perguntar.


(Matheus Verissimo) #16

Perfeito, vou usar essa lib pra vida rs, funcionou conforme desejado, muito obrigado novamente, o que precisar de mim rs só chamar.


(Douglas Oliveira Garrido) #17

Sim, eu uso ela há um bom tempo, pois como tenho conhecimento em C#, é praticamente a mesma coisa.

Por nada, precisando, estou ai pra ajudar! :grin:


(jordyn) #18

Show a primeira opção funcionou perfeitamente. muito obrigado pela ajuda. Esta ferramenta online é útil: json formatter https://jsonformatter-online.com