Merging an array with an object array

Asked

Viewed 120 times

-2

let apis = [{id: "1", name"api 1"}, {id: "2", name:"api 2"}, {id: "3", name:"api 3"}];


let applications = [{id: "1", name:"application 01", apis: ["1", "2"]}, {id: "2", name: "application 02", apis: ["2", "3"]}]

I’d like a result like that:

 let resultado = [
{id: "1", name: "application 01", apis: [{id: "1", name:"api 1"}, {id: "2", name:"api 2"}]}, 
{id: "2", name: "application 02", apis: [{id: "2", name:"api 2"}, {id: "3", name:"api 3"}]}]

I am wanting each api to enter its respective application. I’m a little confused on the ideas still, I’m thinking you’ll probably have to use a reduce, but I don’t know how... Could you help me please

5 answers

4

You need to iterate through all 'Applications' elements and for each of them to check all 'Apis'':

let apis = [{id: "1", name: "api 1"}, {id: "2", name:"api 2"}, {id: "3", name:"api 3"}];
let applications = [{id: "1", name:"application 01", apis: ["1", "2"]}, {id: "2", name: "application 02", apis: ["2", "3"]}];

//pasando por todos os 'applications'
resultado = applications.map(function(app) {
  //cada iteração, aqui dentro, um dos elementos de 'applications' é chamado de 'app'
  //abaixo, filtrando as 'apis' conforme o vetor 'apis' dentro do 'app'
  apisSelecionadas = apis.filter(function(api) {
    //só retorna do 'apis' (principal) os elementos que existam dentro da propriedade 'apis' do 'app' 
    return app.apis.indexOf(api.id) !== -1;
  });
  //coloco o resultado no 'app'
  app.apis = apisSelecionadas;

  return app;
});

console.log(resultado)

  • has circular reference in the object

3

An option would be to iterate over each element of applications and, for each iteration, search in the array apis, the object api correspondent:

function groupApplicationApi(applications, apis) {
  // Cria um novo array para não modificar o original:
  const resultingArray = [];

  for (const application of applications) {
    // Também criamos um novo objeto para não modificar o original:
    const applicationObj = { ...application };
    const apiObjs = [];

    for (const apiId of applicationObj.apis) {
      const apiObj = apis.find((api) => api.id === apiId);

      // Se não existir API correspondente, não faça nada:
      if (!apiObj) {
        continue;
      }

      apiObjs.push(apiObj);
    }

    // Adiciona as apis ao novo objeto:
    applicationObj.apis = apiObjs;

    // Adiciona o novo objeto no array resultante:
    resultingArray.push(applicationObj);
  }

  return resultingArray;
}

const apis = [
  { id: '1', name: 'api 1' },
  { id: '2', name: 'api 2' },
  { id: '3', name: 'api 3' }
];

const applications = [
  { id: '1', name: 'application 01', apis: ['1', '2'] },
  { id: '2', name: 'application 02', apis: ['2', '3'] }
];

console.log(
  groupApplicationApi(applications, apis)
);

But, in general, the above algorithm is not very performatic, since for each element of applications, iterate (worst-case) on each element of apis.

Thus, the complexity is O(m * n), being m the number of elements of applications and n the number of elements of apis.

You can do in O(m + n), which is undoubtedly better - despite causing slight cost in memory, since we will have to maintain a dictionary with each element of apis available, indexed by its ID.

Anyway, so:

function groupApplicationApi(applications, apis) {
  const apiDict = Object.create(null); // Cria o dicionário:
  for (const api of apis) {
    apiDict[api.id] = api;
  }

  const resultingArray = [];
  for (const application of applications) {
    const applicationObj = { ...application };
    
    const apiObjs = [];
    for (const apiId of applicationObj.apis) {
      // Note que não utilizamos o `find` (O(n)), apenas uma indexação (O(1)).
      const apiObj = apiDict[apiId];

      if (!apiObj) {
        continue;
      }

      apiObjs.push(apiObj);
    }

    applicationObj.apis = apiObjs;
    resultingArray.push(applicationObj);
  }

  return resultingArray;
}

const apis = [
  { id: '1', name: 'api 1' },
  { id: '2', name: 'api 2' },
  { id: '3', name: 'api 3' }
];

const applications = [
  { id: '1', name: 'application 01', apis: ['1', '2'] },
  { id: '2', name: 'application 02', apis: ['2', '3'] }
];

console.log(
  groupApplicationApi(applications, apis)
);


Note that in the two examples above, I avoided trying to modify the original elements. Whenever a modification was required in the code, it was made in a "copy", not in the original. However, the references to the objects of apis were maintained.

  • has circular reference in the object

  • How, @Ricardopunctual - could you try to detail more? Circular reference seems impossible to me in the above code.

  • if you run the snippet you will see that it appears "/ref:5/,"

  • 2

    And what does that have to do with circular reference? As far as I know, it just seems to me a reference to an object - not a circular reference.

  • I wrote reference, it’s circular dependency Sorry :) that’s the same as "[circular Object]"

  • Okay, let me ask you - where are you going with this? Is there a problem?

  • exactly what I commented, just illustrate that there is circular dependency in the code, then it is up to those who use the example to think if it makes a difference or not

  • 2

    @Ricardopunctual, I think it’s wrong to use the term "circular". There’s nothing circular (there’s nothing circular dependency and neither circular reference!) in the code above. I still think you mean "reference" (which actually happens).

  • tbm I think the term is not the best, even I even made a mistake when writing "circular dependency" as "circular reference", but that’s what it’s called, I think for lack of another, because the term "circular dependency" is more used in the concept of packages

  • 2

    @Ricardopunctual, no, it’s really neither of those two terms, because, as I said, it has nothing to do with any of the codes in this question. What is happening is the maintenance of a simple reference. The term is therefore "reference" - and only. You have to be careful not to confuse...

  • 2

    @Ricardopontual Esse /**ref:5**/ is simply to indicate that the object that is there is the same as the object that is previously at another point (in the case, higher up, where it has /**id:5**/). But this is not necessarily circular, because the same reference to the same object can be in several places and give no problem. It would be circular if object A has a reference to B, which in turn has a reference to A (one way to know if there is a problem is to try to convert to JSON). Here is an example: https://repl.it/@hkotsubo/Circularreferences#index.js

  • 1

    put an answer to try to exemplify better, I understand that the term "sounds strange", but I just want to say that, in doing this, we have a reference to the same object, was only and only that from the beginning

  • It’s not "sound weird," it’s a wrong term, @Ricardopunctual. It’s the same thing as calling a beet potato. It’s kind of similar, but it’s wrong. There’s no circular reference here.

  • good @Luizfelipe I will not discuss the nomenclature with you, as I said is up in the links of the OS, if you do not agree can comment there, I’m only using what is there and on the internet, again, I am not the one who baizo things :)

  • The OS link is right, you just referenced it in an incorrect context.

  • 1

    @Ricardopunctual "we have a reference to the same object" - yes, but the question does not make it clear whether it was to be a copy of the object or the same. Maybe it is worth warning that because of this, a change in the object also affects the result (as you did). But in the case of this response, the reference is not circular. Circular is when an object A points to B and B points to A. Here we have an array with 2 different elements pointing to the same object (i.e., we have A and B pointing to C, there is no "circle closing"). It’s just the same reference used in various places, period :-)

  • 2

    And just for the record, I left an observation at the end of my reply to get the reader’s attention about the copies that have been (and perhaps need to be) made. cc: @Ricardopunctual

  • thanks @Luizfelipe, and again, there is no way any criticism to any of the answers, I myself was going to start writing a reply, but had already answered the ideas I had, was just to comment on the reference itself, maybe the generated object will be read only, and all that won’t even matter

  • I understand, @Ricardopunctual. The biggest problem that I see in the comments is that an incorrect term was used (which can certainly cause some confusion). To recap this, it’s all right. =)

Show 14 more comments

3

Another alternative would be to index each element of the array apis for his id and use each value of applications.apis key to this index.

let apis = [
  {id: "1", name: "api 1"}, 
  {id: "2", name: "api 2"}, 
  {id: "3", name: "api 3"}
];

let applications = [
  {id: "1", name: "application 01", apis: ["1", "2"]}, 
  {id: "2", name: "application 02", apis: ["2", "3"]}
];
   
let index = [];
let result = [];

//Indexa cada elemento de apis pelo seu id.
for(let e of apis){
  index[e.id] = e;  
  //index[e.id] = {...e};  //caso queira criar um objeto novo ao invés de referenciar os objetos já existentes use essa linha. 
}

//Para cada elemento de applications...
for(let e of applications){
  let apis=[]                          //...inicializa a lista que receberá as substituições para esse elemento.
  //...para cada um dos valores em e.apis...
  for(let v of e.apis){
    apis.push(index[v])                //...obtém o elemento indexado pelo valor e salva em apis.
  }
  //Monta o resultado.
  result.push({"id":e["id"],"name":e["name"],"apis":apis})
}

for(var e of result){console.log(e)}

The same code above in functional version.

let apis = [
  {id: "1", name: "api 1"}, 
  {id: "2", name: "api 2"}, 
  {id: "3", name: "api 3"}
];

let applications = [
  {id: "1", name: "application 01", apis: ["1", "2"]}, 
  {id: "2", name: "application 02", apis: ["2", "3"]}
];

let index = apis.reduce((r,e)=>{
  r[e.id]= e;
  //r[e.id] = {...e};  //caso queira criar um objeto novo ao invés de referenciar os objetos já existentes use essa linha.
  return r;
},[]);

let result = applications.reduce((r,e)=>{
  let apis= e.apis.reduce((r,e)=>{
    r.push(index[e]);
    return r
  },[]);   
  r.push({"id":e["id"],"name":e["name"],"apis":apis})
  return r
},[]);    

for(var e of result){console.log(e)}

  • 1

    What if the Ids are spaced out? Then the array will stay bumpy? :P I always forget this technique of using the array (which always amazes me because it is very interesting), but in this case it would not be better to use the indexes (even index) of each element of the array apis?

  • 1

    @Luizfelipe The index array is really bumpy. To tell the truth it already starts bumpy, the index 0 is the first hole.

  • console.log(index) will return something like this(I’m doing in my head forgive the faults): [undefined, {id: "1", name: "api 1"}, {id: "2", name: "api 2"}, {id: "3", name: "api 3"}]

  • 1

    Yes - when using id, in practice, it is the same thing as using an object. :-)

  • I get your point @Luizfelipe, but I do it thinking the array apis may not be with ordered entries, ex: let apis = [{id: "3", name: "api 3"},{id: "1", name: "api 1"},{id: "2", name: "api 2"}];

  • has circular reference in the object

  • 1

    @Ricardopunctual there is no circular reference. What happens is that when doing console.log(result) browser creates a tree of references for nested objects and prints the first occurrence and just marks where repetitions would be inserted. Ref: https://developer.mozilla.org/en-US/docs/Web/API/Console/log#logging_objects

  • @Augustovasques you used the JSON to serialize that "masks" this. Do the following, before the line that serializes the code put this result[0].apis[1].name = "bananas"; and see how the object looks. Edit the question and add this pf

  • 2

    @Ricardopunctual, that’s it nay means circular reference. References are the same and because of that, this is an expected behavior (and again, it’s not about circular behavior).

  • Even so the AP is asking I am wanting each api to enter its respective application. create a new object r[e.id]= {...e} she loses the references of the original.

  • guys, again I’m not the one who names things, is the only way that cyclic depencencia are called, has several texts about it for example https://stackoverflow.com/questions/14962018/detecting-and-fixing-circular-references-in-javascript, and that https://dev.to/salyadav/deep-clone-of-jsobjects-with-circular-dependency-4if7 I just want to warn that when setting a reference of the object to itself in a nested object will have a circular reference, but treat as you want the theme, is not critical the answers, I think everyone misunderstood

  • You are getting it wrong. Circular references do exist, but (I repeat) that is not what is happening here...

  • 2

    There is no cyclical dependency. Do the following for(var e of result){console.log(e)} and see for yourself. If you do this JSON.stringify(result) in a cyclically referenced object the program hangs.

  • @Luizfelipe read the links, if you think it’s wrong comment there, again, I’m just commenting for a detail, and this is how I see being called, whether or not here is not the best forum to discuss nomenclature

  • 2

    It’s not wrong there. In the question you linked there really is circle dependency. Only this doesn’t happen here. You are stipulating disconnected links and terms that do not happen here. Read the links referred to in my other comment to understand what is a circular dependency and circular reference.

  • @Augustovasques, if you put the line of code I spoke, you will see that changes the two objects, because they have the same reference. In a language like C# this is clear as water, because it is an object reference, in javascript unfortunately has this name, again I just pointed out no name things :)

  • 1

    What AP asked for. Again this JSON.stringify(result) in a cyclically referenced object gives the error "TypeError: Converting circular structure to JSON"

  • I put in an answer to try to better exemplify

Show 13 more comments

2

Due to the numerous comments, to try to explain better I will write an answer, if you find that help is here, otherwise we can remove.

I’ll take for example the answer code of @Augustovasques:

let apis = [
  {id: "1", name: "api 1"}, 
  {id: "2", name: "api 2"}, 
  {id: "3", name: "api 3"}
];

let applications = [
  {id: "1", name: "application 01", apis: ["1", "2"]}, 
  {id: "2", name: "application 02", apis: ["2", "3"]}
];
   
let index = [];
let result = [];

//Indexa cada elemento de apis pelo seu id.
for(let e of apis){
  index[e.id] = e;
}

//Para cada elemento de applications...
for(let e of applications){
  let apis=[]                          //...inicializa a lista que receberá as substituições para esse elemento.
  //...para cada um dos valores em e.apis...
  for(let v of e.apis){
    apis.push(index[v])                //...obtém o elemento indexado pelo valor e salva em apis.
  }
  //Monta o resultado.
  result.push({"id":e["id"],"name":e["name"],"apis":apis})
}

// Aqui, vai apontar a tal "dependencia circular" com /**id:5**/
console.log(result);
/*
Isso indica que, um mesmo objeto tem referência ao mesmo objeto "filho" (nesse caso, "apis") em pontos diferentes. Isso é demonstrado alterando esse objeto com a mesma referência.
Veja que será alterado um elemento, mas ao serializar, TODAS as referências são alteradas.
*/

console.log("Agora vai ser alterado um único objeto:");

result[0].apis[1].name = "bananas";

console.log(JSON.parse(JSON.stringify(result)))

Notice that when changing a single object, everyone who has the same reference is changed. This in a language with C# is something clear to understand, because objects have reffers to others.

As @Luiz Felipe commented, it is a name that does not fit very well for this, but this is how I have read and this is what is called, for example in these links:

https://stackoverflow.com/questions/14962018/detecting-and-fixing-circular-references-in-javascript

https://dev.to/salyadav/deep-clone-of-js-objects-with-circular-dependency-4if7

I only commented to alert to a common fact and that has several questions including here in Sopt about changing objects with the same reference, for example:

var obj1 = new AlgumaClasse();
obj1.Nome = "Batatas";
obj2 = obj1;
obj2.Nome = "Bananas";

// logo, por referência, obj1.Nome também será igual a "Bananas"
  • 1

    It is precisely this possibility to propagate the change in a hotspot that AP seeks. If it has to change some value on fly it does not need to look for which objects to change, just change the value in apis.

  • For example the api points to https://minhaapi/func/123?format=json and during program operation the server minhaapi out of the air and and the AP has to switch to mirrorminhaapi then only exchange the respective input in the array api for https://mirrorminhaapi/func/123?format=json and change is propagated.

  • 2

    I still don’t understand the problem of keeping references. Of course, when you want to avoid mutations, you copy the value (as I did in my answer when I needed to modify some value - lines 3 and 7). However, the objects of apis not modified, so there is no reason to copy them, since it would entail unnecessary additional memory cost. This behavior of references is common and expected in Javascript and is something that the programmer should pay attention to when working with objects (which are passed by reference).

  • I see no problem @Luizfelipe, as long as that is clear. The code example I put in the answer may be a problem for one person, but for another person it may not be exactly what they need, but it’s important to make it clear. In the question example, we take two objects that have nothing to reference each other and generate a new one with references, it is important to show. Whether it makes a difference or not depends on who you use

  • @Augustovasques I think I understood his point, he went so far in this example kkk but I think that if this were an example of Java or C# for example I would probably write "this example generates a reference by object", and everything would be clearer, the problem that the term "circular dependency" generated much doubt

  • @Ricardopunctual, for example if you do not want to index the references and create a new object for each entry on the line you are on index[e.id] =e; change index[e.id] = {...e}; that the program will behave differently, and change apis[0].name= "banana"; no more effect. Take a +1 not to zero because it is valid point of view.

  • for me gave the same result @Augustovasques :(

  • Don’t use the console.log(result); it will try to optimize and instead of sending the reference to the object it will send the reference to the string, use for(var e of result){console.log(e)}

  • For example, https://repl.it/@Ronaldovasques/Wisewastefulset#index.js

  • Interesting wow, that wheel link nojejs? I tested the question and it was the same, I put here tbm and it was the same thing: https://jsfiddle.net/dry251xc/

  • But you changed the result right away result[0].apis[1].name = "bananas"; in result[0] the output will be bananas, change at the origin: apis[0].name= "banana";. Yes it is Node.

  • 1

    exact @Augustovasques, otherwise you cannot validate the reference :) see that if using result[0].apis[0].name that has no reference works. What matters is the end result after Mapping

  • In case you want to prevent the result of the mapping, after obtained, is later changed this can be frozen with Object.()

Show 8 more comments

0

You can use the map array to map your object according to the model you want to display:

let apis = [
  {id: "1", name: "api 1"}, 
  {id: "2", name: "api 2"}, 
  {id: "3", name: "api 3"}
];

let applications = [
  {id: "1", name: "application 01", apis: ["1", "2"]}, 
  {id: "2", name: "application 02", apis: ["2", "3"]}
];

var collection = [];

 collection[0] = apis.map((val) => {
  return {
     id:val.id,
     name:val.name,
     apis:(val.apis) ? val.apis : []
  }
});

collection[1] = applications.map((val) => {
  return {
     id:val.id,
     name:val.name,
     apis:val.apis
  }
});

console.log(collection)

Browser other questions tagged

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