So I have this object that has two keys, clinics and invitations. I want to associate the clinics with the invitations taking into account the clinicId:
const upcomingClinics = {
"clinics": {
"a0CW000000271LuMAI": {
"id": "a0CW000000271LuMAI",
"contact": {
"name": null,
"phone": null,
"email": null
},
"shifts": {
"teamLeads": 1,
"healthTechs": 1
}
},
"a0CW00000026gikMAA": {
"id": "a0CW00000026gikMAA",
"contact": {
"name": null,
"phone": null,
"email": null
},
"shifts": {
"teamLeads": 1,
"healthTechs": 4
}
}
},
"invitations": {
"56392": {
"id": "56392",
"clinicId": "a0CW00000026gikMAA"
},
"56393": {
"id": "56393",
"clinicId": "a0CW00000026gikMAA"
},
"56402": {
"id": "56402",
"clinicId": "a0CW00000026gikMAA"
},
"56427": {
"id": "56427",
"clinicId": "a0CW000000271LuMAI"
},
"56428": {
"id": "56428",
"clinicId": "a0CW000000271LuMAI"
}
}
}
The keys of the clinics object always match the IDs. Basically I want this object to look like this, because they have in common the same clinicId, how can I do this? Inserting a new key invitations to the clinics object?:
const upcomingClinics = {
"clinics": {
"a0CW000000271LuMAI": {
"id": "a0CW000000271LuMAI",
"contact": {
"name": null,
"phone": null,
"email": null
},
"shifts": {
"teamLeads": 1,
"healthTechs": 1
}
"invitations": {
"56427": {
"id": "56427",
"clinicId": "a0CW000000271LuMAI"
},
"56428": {
"id": "56428",
"clinicId": "a0CW000000271LuMAI"
}
}
},
"a0CW00000026gikMAA": {
"id": "a0CW00000026gikMAA",
"contact": {
"name": null,
"phone": null,
"email": null
},
"shifts": {
"teamLeads": 1,
"healthTechs": 4
}
"invitations": {
"56392": {
"id": "56392",
"clinicId": "a0CW00000026gikMAA"
},
"56393": {
"id": "56393",
"clinicId": "a0CW00000026gikMAA"
},
"56402": {
"id": "56402",
"clinicId": "a0CW00000026gikMAA"
},
}
}
},
}
Thanks!
i think this is what you are looking for https://jsfiddle.net/q4rt6zad/10/
Object.getOwnPropertyNames(upcomingClinics.clinics).forEach((clinicId) => {
upcomingClinics.clinics[clinicId].invitations = {};
Object.getOwnPropertyNames(upcomingClinics.invitations).forEach((id) => {
const invite = upcomingClinics.invitations[id];
if (invite.clinicId === clinicId) {
upcomingClinics.clinics[clinicId].invitations[id] = invite;
}
});
});
delete upcomingClinics.invitations;
Just loop the invitations object, and for each invitation check if its clinic is already included in upcomingClinics object, if so then just add this invitation to its invitations object, otherwise, create a new clinic record in upcomingClinics then insert the current invitation to its invitations object:
let result = Object.keys(upcomingClinics.invitations).reduce(function(result, invitationId) { // for each invitationId in upcomingClinics.invitations object
let invitation = upcomingClinics.invitations[invitationId]; // get the current invitation object
let clinicId = invitation.clinicId; // get its clinicId
if(!result[clinicId]) { // if there is no record of this clinic in the result object
result[clinicId] = Object.create(upcomingClinics.clinics[clinicId]); // create one by cloning the clinic object from upcomingClinics.clinics
result[clinicId].invitations = {}; // create an object that will hold its invitations
}
result[clinicId].invitations[invitationId] = invitation; // add the current invitation to its corresponding clinic object
return result;
}, {});
Example:
const upcomingClinics = {"clinics":{"a0CW000000271LuMAI":{"id":"a0CW000000271LuMAI","contact":{"name":null,"phone":null,"email":null},"shifts":{"teamLeads":1,"healthTechs":1}},"a0CW00000026gikMAA":{"id":"a0CW00000026gikMAA","contact":{"name":null,"phone":null,"email":null},"shifts":{"teamLeads":1,"healthTechs":4}}},"invitations":{"56392":{"id":"56392","clinicId":"a0CW00000026gikMAA"},"56393":{"id":"56393","clinicId":"a0CW00000026gikMAA"},"56402":{"id":"56402","clinicId":"a0CW00000026gikMAA"},"56427":{"id":"56427","clinicId":"a0CW000000271LuMAI"},"56428":{"id":"56428","clinicId":"a0CW000000271LuMAI"}}};
let result = Object.keys(upcomingClinics.invitations).reduce(function(result, invitationId) {
let invitation = upcomingClinics.invitations[invitationId];
let clinicId = invitation.clinicId;
if(!result[clinicId]) {
result[clinicId] = Object.create(upcomingClinics.clinics[clinicId]);
result[clinicId].invitations = {};
}
result[clinicId].invitations[invitationId] = invitation;
return result;
}, {});
console.log(result);
const clinics = {};
for (let clinicId in upcomingClinics.clinics) {
clinics[clinicId] = upcomingClinics.clinics[clinicId];
clinics[clinicId].invitations = {};
for (let invitId in upcomingClinics.invitations) {
const invitation = upcomingClinics.invitations[invitId];
if (invitation.clinicId === clinicId) {
clinics[clinicId].invitations[invitId] = invitation;
}
}
}
https://jsfiddle.net/bg6srahq/
Related
I'm trying to use paularmstrong/normalizr on JSON that comes from FractalTransformer and whose nested childs have "data" attribute. Example of JSON:
{
"data": {
"object": "Offer",
"id": "5g6aqocew4qjzl40",
"real_id": 26,
"name": "Random Name",
"created_at": {
"date": "2019-06-18 11:13:08.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"readable_created_at": "1 year ago",
"site": {
"data": {
"object": "Site",
"id": "65zody8vj29vlegd",
"name": "Test Site",
"real_id": 1
}
},
"countries": {
"data": [
{
"object": "Country",
"code": "US",
"name": "United States"
},
{
"object": "Country",
"code": "DE",
"name": "Germany"
}
]
}
},
"meta": {
"include": [
"site",
"countries"
],
"custom": []
}
}
Schemas I use:
export const offerSchema = new schema.Entity('offers')
export const siteSchema = new schema.Entity('sites', {}, {
processStrategy: (value) => {
return { ...value.data }
},
idAttribute: (value) => {
return value.data.id
},
})
export const countrySchema = new schema.Entity('countries')
offerSchema.define({
site: siteSchema,
countries: [countrySchema],
})
Now the issue is that I remove 'data' from the site since it's just one object successfully, but I can't do it in the country case. Whatever I tried with custom processStrategy fails, as country is object that has data which is array (I assume this is where the issue is, going from Entity to Array). And in idAttribute function I always get complete array so can't determine the ID of single entry. So the end result is that the ID of countries is undefined. Any ides?
I actually managed with another approach. I added processStrategy on the parent, 'Offer' in this case, so all 'data' parts get stripped before they reach other child schemas.
const normalizrStripDataOptions = {
processStrategy: (value) => {
const ret = { ...value }
Object.keys(ret).forEach((key) => {
if (ret[key] !== null) {
if (ret[key].data && Array.isArray(ret[key].data)) {
ret[key] = [...ret[key].data]
}
if (ret[key].data && typeof ret[key].data === 'object') {
ret[key] = { ...ret[key].data }
}
}
})
return ret
},
}
export const offerSchema = new schema.Entity('offers', {}, normalizrStripDataOptions)
export const siteSchema = new schema.Entity('sites')
export const countrySchema = new schema.Entity('countries')
offerSchema.define({
site: siteSchema,
countries: [countrySchema],
})
How can I filler the below array on common id and output should have unique and latest history number
const input = [{
"id": 134116,
"user": "admin",
"historyno": "134116-0"
}, {
"id": 134132,
"user": "admin",
"historyno": "134132-0"
}, {
"id": 134132,
"user": "admin",
"historyno": "134132-1"
}, {
"id": 134133,
"user": "admin",
"historyno": "134133-0"
}, {
"id": 134133,
"user": "admin",
"historyno": "134133-1"
}];
let output = [];
let tempId;
for (let i = 0; i < input.length; i++) {
if (input[i].id === tempId) {
//do nothing
} else {
output.push(input[i]);
tempId = input[i].id;
}
}
console.log(output);
Expected Output
[
{
"id": 134116,
"user": "admin",
"historyno": "134116-0"
},
{
"id": 134132,
"user": "admin",
"historyno": "134132-1"
},
{
"id": 134133,
"user": "admin",
"historyno": "134133-1"
}
]
Reduce the array to a Map, using the id as key, and then convert back by spreading the Map.values() iterator to an array.
This solution assumes that the array is presorted by history numbers:
const input = [{"id":134116,"user":"admin","historyno":"134116-0"},{"id":134132,"user":"admin","historyno":"134132-0"},{"id":134132,"user":"admin","historyno":"134132-1"},{"id":134133,"user":"admin","historyno":"134133-0"},{"id":134133,"user":"admin","historyno":"134133-1"}];
const output = [...input.reduce((r, o) => r.set(o.id, o), new Map).values()];
console.log(output);
This solution handles unsorted arrays by only replacing the current item in the map if historyno is greater:
const input = [{"id":134116,"user":"admin","historyno":"134116-0"},{"id":134132,"user":"admin","historyno":"134132-0"},{"id":134132,"user":"admin","historyno":"134132-1"},{"id":134133,"user":"admin","historyno":"134133-0"},{"id":134133,"user":"admin","historyno":"134133-1"}];
const getHistoryNo = ({ historyno }) => +historyno.split('-')[1];
const output = [...input.reduce((r, o) => {
const prev = r.get(o.id);
if(!prev || getHistoryNo(o) > getHistoryNo(prev)) r.set(o.id, o);
return r;
}, new Map).values()];
console.log(output);
You can split historyno by - and take the second element, compare it with previously set same id's value, if the current one is higher than last one use the current one else use the last one
const input = [{"id": 134116,"user": "admin","historyno": "134116-0"}, { "id": 134132, "user": "admin","historyno": "134132-0"}, { "id": 134132,"user": "admin","historyno": "134132-1"}, { "id": 134133, "user": "admin", "historyno": "134133-0"
}, { "id": 134133,"user": "admin","historyno": "134133-1"}];
let final = input.reduce((op,inp)=>{
op[inp.id] = op[inp.id] || inp
let lastHisotry = +op[inp.id].historyno.split('-')[1]
let currentHistory = +inp.historyno.split('-')[1]
op[inp.id].historyno = currentHistory > lastHisotry ? inp.historyno : op[inp.id].historyno
return op
},{})
console.log(final);
Note:- If your array is already ordered you don't need this split logic, you can simply do
const input = [{"id": 134116,"user":"admin","historyno": "134116-0"}, { "id": 134132, "user":"admin","historyno": "134132-0"}, { "id": 134132,"user": "admin","historyno": "134132-1"}, { "id": 134133, "user": "admin", "historyno": "134133-0"}, { "id": 134133,"user": "admin","historyno": "134133-1"}];
let final = input.reduce((op,inp) => {
op[inp.id] = inp
return op
},{})
console.log(final);
var resultObj = input.reduce((prev, next) => {
if(prev.hasOwnProperty(next.id)) {
const currentLatest = prev[next.id];
// Your logic to check if 'currentLatest' is the latest one or the 'next'
} else {
prev[next.id] = next;
}
return prev;
}, {});
var result = Object.values(resultObj)
Plunker
I have two structures - ingredients and recipes
[{
"id":"1",
"name": "Cucumber"
},
..
]
and
[{
"id":"1",
"name": "Salad1",
"recipein":[1, 3, 5]
}, {
...
}
]
and i want to show names of ingredients in each salad by press a button.
I filtered object to get ID of object, then i try to get a array of ingredients
getSalad(param:number) {
this.saladId = this.recipe.filter(rec => {
return rec.id.includes(param);
})
this.getNameOfIngredients(this.saladId)
}
getNameOfIngredients(saladArray:any) {
var ingredientsId = saladArray.map(function(num) {
return num.recipein;
});
i getting array [1,2,4] now i want to show all names of ingredients from this.ingredients with this array of id's.
How can i do this?
Plunker
I made updates in your plunker. I think thats what are you looking for: Plunker
getSalad(param:number) {
this.saladId = this.recipe.filter(rec => +rec.id === param )[0];
if(!this.saladId){
this.currentSalad = "Salad not found";
return;
}
this.currentSalad = this.getNameOfIngredients(this.saladId)
}
getNameOfIngredients(saladArray:any) {
return this.ingredients.filter( ing => {
return saladArray.recipein.indexOf(+ing.id) !== -1;
});
let _ingredients = []
this.ingredients.foreach((ingr)=>{
if(this.ingreIDArry.indexof(ingr.id) > -1){
_ingredients.push(ingr.name)
}
})
return _ingredients
is this what you want?
if you can flatten the array, it would be very straightforward for us to do lookups.
Here is what you could do.
const salads = [{
"id": "1",
"name": "Salad1",
"recipein": [1, 3, 5]
}];
const ingredients = [{
"id": "1",
"name": "Cucumber"
},
{
"id": "2",
"name": "Cucumber2"
},
{
"id": "3",
"name": "Cucumber3"
},
{
"id": "4",
"name": "Cucumber4"
},
{
"id": "5",
"name": "Cucumber5"
}
];
const flattenIngredients = (() => {
const output = {};
ingredients.forEach((ingredient) => {
output[ingredient.id] = ingredient;
});
return output;
})();
const getSalad = (saladId) => {
const filteredSalad = salads.filter((salad) => {
return saladId == salad.id;
});
if (filteredSalad.length > 0) {
const salad = filteredSalad[0];
return salad.recipein.map((receip) => flattenIngredients[receip].name);
}
}
console.log(getSalad(1));
I need to merge two JSON objects.
First object:
var objectA = {
"UUID1": {
"user": {
"ID": "1"
}
},
"UUID2": {
"user": {
"ID": "2"
}
},
"UUID3": {
"user": {
"ID": "3"
}
}
}
Second object:
var objectB = {
"UUID4": {
"user": {
"ID": "4"
}
},
"UUID5": {
"user": {
"ID": "3"
}
},
"UUID6": {
"user": {
"ID": "2"
}
}
}
Expected result:
{
"UUID1": {
"user": {
"ID": "1"
}
},
"UUID2": {
"user": {
"ID": "2"
}
},
"UUID3": {
"user": {
"ID": "3"
}
},
"UUID4": {
"user": {
"ID": "4"
}
}
}
The trick is, the UUID will differ, but the primary key is the user ID. So, I need to compare the user IDs and keep only one UUID.
Is there a clever way on how to approach this? Nested loops using Object.keys(objectX).forEach did not work for me well :(
Thank you!
You can just create custom function to handle this for you. Something like this:
var objectA = {
"UUID1": {"user": {"ID": "1"}},
"UUID2": {"user": {"ID": "2"}},
"UUID3": {"user": {"ID": "3"}}
}
var objectB = {
"UUID4": {"user": {"ID": "4"}},
"UUID5": {"user": {"ID": "3"}},
"UUID6": {"user": {"ID": "2"}}
}
function merge() {
var result = {};
var ids = [];
for (var i = 0; i < arguments.length; i++) {
for (var uuid in arguments[i]) {
if (~ids.indexOf(arguments[i][uuid].user.ID)) {
continue;
}
result[uuid] = arguments[i][uuid];
ids.push(arguments[i][uuid].user.ID);
}
}
return result;
}
var merged = merge(objectA, objectB);
console.log(merged);
You could take a hash table for remembering the inserted ID and build a new object out of the given objects.
var objectA = { UUID1: { user: { ID: "1" } }, UUID2: { user: { ID: "2" } }, UUID3: { user: { ID: "3" } } },
objectB = { UUID4: { user: { ID: "4" } }, UUID5: { user: { ID: "3" } }, UUID6: { user: { ID: "2" } } },
hash = Object.create(null),
result = [objectA, objectB].reduce(function (r, o) {
Object.keys(o).forEach(function (k) {
if (!hash[o[k].user.ID]) {
hash[o[k].user.ID] = true;
r[k] = o[k];
}
});
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);