I have an object with details - name, email, contact with unique ID
{
email_IcWDC6iyNzsQe9sPss4Yy: "xyz#gmail.com"
email_QVXeOX1vijCtpx8mdD662: "abc#gmail.com"
mobile_IcWDC6iyNzsQe9sPss4Yy: "223"
mobile_QVXeOX1vijCtpx8mdD662: "122"
name_IcWDC6iyNzsQe9sPss4Yy: "mp"
name_QVXeOX1vijCtpx8mdD662: "pp"
}
for example: name_QVXeOX1vijCtpx8mdD662 this contains name_UniqueID but remains same for one set of form for name, email, mobile. These values are taken from as many forms generated in the frontend, form has unique Id and that has been pass to their fields, so how can I make a new collection of objects containing a name, email, mobile for a particular unique id as given.
example:
[
{
id: "IcWDC6iyNzsQe9sPss4Yy"
name: "mp",
email: "",
mobile_no: "",
},
{
id: "QVXeOX1vijCtpx8mdD662"
name: "pp",
email: "",
mobile_no: "",
},
]
One way of doing it:
const d = {
email_IcWDC6iyNzsQe9sPss4Yy: "xyz#gmail.com",
email_QVXeOX1vijCtpx8mdD662: "abc#gmail.com",
mobile_IcWDC6iyNzsQe9sPss4Yy: "223",
mobile_QVXeOX1vijCtpx8mdD662: "122",
name_IcWDC6iyNzsQe9sPss4Yy: "mp",
name_QVXeOX1vijCtpx8mdD662: "pp"
};
const result = Object.values(
Object.entries(d).reduce((acc, [k, v]) => {
const [field, id] = k.split("_", 2);
if (!acc[id]) acc[id] = {id};
acc[id][field] = v;
return acc;
}, {})
);
console.log(result);
const input= {
email_IcWDC6iyNzsQe9sPss4Yy: "xyz#gmail.com",
email_QVXeOX1vijCtpx8mdD662: "abc#gmail.com",
mobile_IcWDC6iyNzsQe9sPss4Yy: "223",
mobile_QVXeOX1vijCtpx8mdD662: "122",
name_IcWDC6iyNzsQe9sPss4Yy: "mp",
name_QVXeOX1vijCtpx8mdD662: "pp"
}
const output=[];
for(let key in input||{}){
if(key.startsWith('name_')){
const id =key.replace('name_','');
const obj={
id,
name:input[key],
email:input[`email_${id}`],
mobile:input[`mobile_${id}`]
};
output.push(obj);
}
}
console.log('output json', output)
you can use dependecies called UUID or NanoId from NPM. this dependencies can be used for an unique ID.
to apply it use:
const idVar = uuidv4() || nanoid();
const obj = {
['id_' + idVar]: 'abcdefg2312',
['name_' + idVar]: "pp",
['email_' + idVar]: "",
['mobileNo_' + idVar]: "",
}
Not very efficient, but could be a sol:
const parentObj = {
email_IcWDC6iyNzsQe9sPss4Yy: "xyz#gmail.com",
email_QVXeOX1vijCtpx8mdD662: "abc#gmail.com",
mobile_IcWDC6iyNzsQe9sPss4Yy: "223",
mobile_QVXeOX1vijCtpx8mdD662: "122",
name_IcWDC6iyNzsQe9sPss4Yy: "mp",
name_QVXeOX1vijCtpx8mdD662: "pp"
};
let resultArr = [];
for (const key in parentObj) {
const template = {
id:"",
name:"",
email:"",
mobile:"",
};
let [prop,uid] = key.split("_");
if(!resultArr[uid]){
template.id = uid;
resultArr[uid] = template;
}
resultArr[uid][prop] = parentObj[key];
}
console.log(resultArr);
Related
Suppose I have an array of Object as:
const studyTime = [
{
user: "john",
readingPeriod: 8681,
},
{
user: "john",
readingPeriod: 8867,
},
{
user: "naresh",
readingPeriod: 22321,
},
{
user: "naresh",
readingPeriod: 1211,
},
{
user: "naresh",
readingPeriod: 1213,
},
{
user: "Samul",
readingPeriod: 1212,
},
{
user: "Samul",
readingPeriod: 1221,
},
];
I want to generate average reading time for each user as:
O/P : {"john":8774,"naresh":8248.3,"Samul:1216.5}
For this I tried: From SO average calculation for each object in array of object
var sum = {};
for(var i = 0; i < studyTime.length; i++) {
var ele = studyTime[i];
if (!sum[ele.user]) {
sum[ele.user] = {};
sum[ele.user]["sum"] = 0;
sum[ele.user]["count"] = 0;
}
sum[ele.user]["sum"] += ele.readingPeriod;
sum[ele.user]["count"]++;
}
var result = [];
for (var user in sum) {
result.push({user: user, readingPeriod: sum[user]["sum"] / sum[user]["count"]});
}
but it gives the O/P as:
[{user: "john", readingPeriod: 8774},{user: "naresh", readingPeriod: 8248.333333333334},{user: "Samul", readingPeriod: 1216.5}]
Is there a way that I can combine two values of each object in an array to form key value like:
{user: "john", readingPeriod: 8774} => {"john":8774}
Let me know how can I achieve expected output as :
{"john":8774,"naresh":8248.3,"Samul:1216.5}
If any other information needed please do let me know.
While you could map the result array to an array of entries that you create an object with via Object.fromEntries, it'd be simpler to do it just starting from the sum object:
const studyTime=[{user:"john",readingPeriod:8681},{user:"john",readingPeriod:8867},{user:"naresh",readingPeriod:22321},{user:"naresh",readingPeriod:1211},{user:"naresh",readingPeriod:1213},{user:"Samul",readingPeriod:1212},{user:"Samul",readingPeriod:1221}];
const sum = {};
for (const { user, readingPeriod } of studyTime) {
sum[user] ??= { count: 0, sum: 0 };
sum[user].count++;
sum[user].sum += readingPeriod;
}
const output = Object.fromEntries(
Object.entries(sum).map(([user, { count, sum }]) => [
user, // key in new object
(sum / count).toFixed(1) // value in new object
])
);
console.log(output);
We can also achieve this using Array.reduce.
const studyTime = [{user:"john",readingPeriod:8681},{user:"john",readingPeriod:8867},{user:"naresh",readingPeriod:22321},{user:"naresh",readingPeriod:1211},{user:"naresh",readingPeriod:1213},{user:"Samul",readingPeriod:1212},{user:"Samul",readingPeriod:1221}];
const findAvgTimes = (data) => {
//Reduce the input data and convert an object
//with key as each user's name and the value
//is the entire object along with the count
const formattedData = data.reduce((res, obj) => {
let key = obj.user;
//if the user data is already is available in the
//result object, then add the `readingPeriod` of res
//with the current object and also increment the count
if(res[key]) {
res[key] = {
...res[key],
readingPeriod: res[key].readingPeriod + obj.readingPeriod,
count: res[key].count + 1
}
}
//if the user data is not available, then add the
//object and add count as 1
else {
res[key] = {...obj, count: 1}
}
return res;
}, {});
//console.log("formattedData: ", formattedData);
//Again use the reduce and then calculate the avg
const avgs = Object.values(formattedData).reduce((res, obj) => {
return {...res, ...{[obj.user]: obj.readingPeriod/obj.count} };
}, {});
return avgs;
}
console.log("final output: ",findAvgTimes(studyTime))
.as-console-wrapper {
max-height: 100% !important;
}
You can do it in 3 little steps:
create an array of unique usernames used in studyTime
extract the average studytime for each username from studyTime in a function
map each username in the array into the objects you want
const studyTime = [
{
user: "john",
readingPeriod: 8681,
},
{
user: "john",
readingPeriod: 8867,
},
{
user: "naresh",
readingPeriod: 22321,
},
{
user: "naresh",
readingPeriod: 1211,
},
{
user: "naresh",
readingPeriod: 1213,
},
{
user: "Samul",
readingPeriod: 1212,
},
{
user: "Samul",
readingPeriod: 1221,
},
];
function calculate_average_study_time(username) {
const study_times = studyTime.filter( study_time => study_time.user === username );
const total = study_times.reduce( (acc, study_time) => study_time.readingPeriod+acc, 0);
return total / study_times.length;
}
// extract users from array into an array of users with only their names
const usernames = studyTime.reduce( (acc, current) => {
if( acc.includes( current.user ) === false )
acc.push( current.user );
return acc;
}, [] );
// transform usernames into the final object with the averages
const result = usernames.map( username => {
let object = {};
object[username] = calculate_average_study_time( username );
return object;
});
console.log(result)
I am stuck with mapping in array of objects.
Please find the below code
const array = [
{
user: "User1",
cities: ["city1", "city2", "city3"],
},
{
user: "User2",
cities: ["city2", "city3", "city4"],
},
];
let x = {};
array.map((item) => {
let user = item.user;
let cities = item.cities;
cities.map((city) => (x[city] = user));
});
Now it returns like this:
const resArray = [{ city1: "User1", city2: "User2", city3: "User2", city4: "User2" }]
I want the array like this:
const resArray = [
{ city1: ["User1"] },
{ city2: ["User1", "User2"] },
{ city3: ["User1", "User2"] },
{ city4: ["User2"] },
];
Can anyone please help me out.
Thanks
Try this
let x = {};
array.forEach((item) => {
item.cities.forEach((city) => {
x[city] = item.cities.includes(city) ? [...x[city] ? x[city] : [], item.user] : [];
});
});
You have been assigning user to city each time. Instead the x[city] should be an array and you should push the new user inside that array.
Try this,
const array = [
{
user: "User1",
cities: ["city1", "city2", "city3"],
},
{
user: "User2",
cities: ["city2", "city3", "city4"],
},
];
let x = {};
array.map((item) => {
let user = item.user;
let cities = item.cities;
cities.map((city) => {
if(x[city] && x[city].length) {
x[city].push(user);
} else{
x[city] = [user];
}
});
});
const res = Object.keys(x).map(key => { return {[key]: x[key]}});
console.log(res);
I am trying to iterate (with a for..in loop) multiple entries and output them as a json object. Where I am running into difficulty is that the data only outputs a single list entry, and not all of them. Using AxiosJS in Node, I've found it outputs in the exact format I'd like. I'm unsure how to format my loop in order to output like this:
{
"Cardio_ETC": {
"name": "John",
"shift": "7a-7a",
"service": "ETC Cardiology",
"Office": "1234",
"cell": ""
},
"Cardio_STEMI": {
"name": "Pran",
"shift": "7a-7a",
"service": "STEMI Cardiology",
"Office": "34561321",
"cell": ""
},
"fastTrack1": {
"name": "Bob",
"shift": "7a-7p",
"service": "Fasttrack",
"Office": "X533 tel",
"cell": "X533"
},...etc
Here is my current code:
.then((data)=>{
connection.query("SELECT * FROM amion_onCall", function (err, result, fields){
//format results here
var fixed = new Object();
let i;
for (i in result){
aName = result[i].name;
serv = result[i].specialty;
aServ = serv.replace(' ','_');
aShift = result[i].shift;
aOff = result[i].office;
aCell = result[i].cell;
aTag = result[i].tag;
var data = {name:aName, service: aServ, shift: aShift, office: aOff, cell: aCell, tag: aTag};
Object.assign(fixed, data);
console.log(fixed);
What is the best way to format this for..in loop in order to output the above json?
In your case, If you want to use for in you can do this
const obj = {};
for (i in result) {
obj[result[i].tag] = {
name: result[i].name,
service: result[i].specialty.replace(' ', '_'),
shift: result[i].shift,
office: result[i].office,
cell: result[i].cell,
}
}
console.log(obj) // will have your answer
but I recommend using reduce
.then((data) => {
connection.query("SELECT * FROM amion_onCall",
function (err, result) {
const obj = result.reduce((o, item) => {
o[item.tag] = {
name: item.name,
service: item.specialty.replace(' ', '_'),
shift: item.shift,
office: item.office,
cell: item.cell,
}
return o
}, {});
console.log(obj)
})
})
I have an object like below
{
UserId: "",
BidderId: "",
"1stDomestic.BidderId": "",
"1stDomestic.UserId": "234",
"1stEmployee.CreatedDate": "",
"1stIndependent.UpdatedDate": "",
"1stIndependent.CreateDate": ""
}
The requirement is such that I need to group the dotted object keys and create the output as below
{
UserId: "",
BidderId: "",
1stDomestic: [
{
BidderId="",
UserId="234"
}
],
1stEmployee: [
{
CreatedDate=""
}
],
1stIndependent: [
{
UpdatedDate="",
CreatedDate=""
}
],
lstDomestic.BidderId = "",
1stDomestic.UserId="234",
1stEmployee.CreatedDate="",
1stIndependent.UpdatedDate=""
1stIndependent.CreateDate=""
}
I have tried to achieve this using couple of approaches.
Here requestedData is the object
Approach 1
for (let prop in requestedData) {
if (prop.indexOf(".") > -1) {
mainKey[prop.split(".").pop()] = requestedData[prop];
requestedData[prop.substr(0, prop.indexOf("."))] = [mainKey];
}
}
console.log(requestedData)
The above approach gives me the structure, but the array data reflects the same for all.
1stDomestic: [
{
BidderId="",
UserId="234",
CreatedDate="",
UpdatedDate=""
}
],
1stEmployee: [
{
BidderId="",
UserId="234",
CreatedDate="",
UpdatedDate=""
}
],
1stIndependent: [
{
BidderId="",
UserId="234",
CreatedDate="",
UpdatedDate=""
}
]
Approach 2
for (let prop in requestedData) {
if (prop.indexOf(".") > -1) {
arr.push({
newProp: prop.substr(0, prop.indexOf(".")), //-->1
mainKey: prop.split(".").pop(), // --> 2
value: requestedData[prop] // -->3
});
}
}
console.log(Object.assign(requestedData, groupData(arr));
groupData(arrVal) {
let key = "newProp";
return resData.reduce((previous, current) => {
previous[current[key]] && previous[current[key]].length != 0
? previous[current[key]].push(current)
: (previous[current[key]] = new Array(current));
return previous;
}, {});
}
The above approach groups the data based on the keys, but then it creates and individual arrays of object with properties as in 1,2 and 3
I expect this to be the way as mentioned above.
I am kind of now in a fix and trying to figure that out.
I am new to this forum, asking question, please bear if I somehow made this question too lengthy and intuitive.
Help would be appreciated
You can first create an object of nested objects based on the keys using reduce and then merge your original object with the nested object to get your final result:
const data = {
UserId: "",
BidderId: "",
"1stDomestic.BidderId": "",
"1stDomestic.UserId": "234",
"1stEmployee.CreatedDate": "",
"1stIndependent.UpdatedDate": "",
"1stIndependent.CreateDate": ""
};
const nested = Object.entries(data)
.filter(([k, v]) => k.includes('.'))
.reduce((acc, [k, v]) => {
const [parent, child] = k.split('.');
acc[parent] = acc[parent] || [{}];
acc[parent][0][child] = v;
return acc;
}, {});
const result = { ...data, ...nested};
console.log(result);
You can use below solution.
var requestedData = {
UserId: "",
BidderId: "",
"lstDomestic.BidderId" : "",
"lstDomestic.UserId":"234",
"lstEmployee.CreatedDate":"",
"lstIndependent.UpdatedDate":"",
"lstIndependent.CreateDate":""
}
var newData = [];
var previousKey = "";
for (let prop in requestedData) {
if (prop.indexOf(".") > -1) {
if(previousKey != prop.substr(0, prop.indexOf(".")))
{
ainKey = [];
}
previousKey = prop.substr(0, prop.indexOf("."))
mainKey[prop.split(".").pop()] = requestedData[prop];
newData[prop.substr(0, prop.indexOf("."))] = [mainKey];
}
}
console.log(newData)
you can try live working demo.
https://jsfiddle.net/cakp8z6n/4/
If you call your original object obj, this should work:
Object.keys(obj).forEach(key => {
if (key.includes('.')) {
const [base, suffix] = key.split('.');
obj[base] = obj[base] || [{}];
obj[base][0][suffix] = obj[key];
}
});
console.log(obj);
Or, if you don't want to modify the original object, but make a modified copy instead:
const obj2 = {};
Object.keys(obj).forEach(key => {
obj2[key] = obj[key];
if (key.includes('.')) {
const [base, suffix] = key.split('.');
obj2[base] = obj2[base] || [{}];
obj2[base][0][suffix] = obj[key];
}
});
let orgObj={
UserId: "",
BidderId: "",
"lstDomestic.BidderId": "",//difference 1st and Ist
"1stDomestic.UserId": "234",//difference 1st and Ist
"1stEmployee.CreatedDate": "",
"1stIndependent.UpdatedDate": "",
"1stIndependent.CreateDate": ""
};
let complexKeys = Object.keys(orgObj).filter(key=>{return key.match(/.+\..+/)})
So complexKeys now ["lstDomestic.BidderId", "1stDomestic.UserId", "1stEmployee.CreatedDate", "1stIndependent.UpdatedDate", "1stIndependent.CreateDate"]
complexKeys.forEach(eachCompleKey=>{debugger;
let firstPart= eachCompleKey.match(/^(\w+)\./)[1];
let lastPart= eachCompleKey.match(/\.(\w+)$/)[1];
if(orgObj[firstPart]==undefined){debugger;
orgObj[firstPart]=[{}];
}
orgObj[firstPart][0][lastPart]=orgObj[eachCompleKey]
})
console.log(orgObj)
Output
{
"UserId": "",
"BidderId": "",
"lstDomestic.BidderId": "",
"1stDomestic.UserId": "234",
"1stEmployee.CreatedDate": "",
"1stIndependent.UpdatedDate": "",
"1stIndependent.CreateDate": "",
"lstDomestic": [
{
"BidderId": ""
}
],
"1stDomestic": [
{
"UserId": "234"
}
],
"1stEmployee": [
{
"CreatedDate": ""
}
],
"1stIndependent": [
{
"UpdatedDate": "",
"CreateDate": ""
}
]
}
You can try something like below.
let obj = {
UserId: "",
BidderId: "",
"lstDomestic.BidderId": "",
"1stDomestic.UserId": "234",
"1stEmployee.CreatedDate": "",
"1stIndependent.UpdatedDate": "",
"1stIndependent.CreateDate": ""
};
const res = Object.keys(obj).reduce((acc, mainKey) => {
let [key1, key2] = mainKey.split(".");
if (key2 && acc[key1]) {
acc[key1][0] = { ...acc[key1][0],
...{
[key2]: obj[mainKey]
}
}
} else if (key2) {
acc[key1] = [{
[key2]: obj[mainKey]
}];
} else {
acc[key1] = obj[key1];
}
return acc;
}, {})
const finalResult = { ...obj,
...res
};
console.log(finalResult)
So this code will do it, but I'm confused why you want it in this format. It seems like it would make more sense to have 1stDomestic, 1stEmployee, and 1stIndependent be their own objects rather than single element arrays. It just requires you to do more work later to access the data!
var requestedData = {
UserId: "",
BidderId: "",
"lstDomestic.BidderId" : "",
"lstDomestic.UserId":"234",
"lstEmployee.CreatedDate":"",
"lstIndependent.UpdatedDate":"",
"lstIndependent.CreateDate":""
}
let output = {};
for (let prop in requestedData) {
// Check to see if this is a "blah.thing" property
if (prop.includes(".")) {
let props = prop.split(".");
// Check to see if we've added the array already
if (output[props[0]])
output[props[0]][0][props[1]] = requestedData[prop];
else
// ES 2015 baby!
output[props[0]] = [{[props[1]]: requestedData[prop]}]
}
// If it's not then just add it normally
else
output[prop] = requestedData[prop];
}
console.log(output);
I need to group by sector and company-name of below json object.
original JSON object:
data =[
{"sector":"IT",
"company-name": "ABC",
"contact": "Person1",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC",
"contact": "Person2",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person1",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person2",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person3",
"position":"Accountant"
},
{"sector":"Finance",
"company-name": "Fin1",
"contact": "Person3",
"position":"Accountant"
}
]
output:
I could able to group by one i.e. company-name.Not how to extend this dynamically for multiple groups.
const result = {};
const groubyfiled = 'company-name'
this.data.foreach((item: any) => {
const gname = item[groubyfiled];
result[gname] = result[gname] || {};
result[gnmae].items = result[gname].items || [];
result[gname].items.push(item);
});
output:
{
"IT": {
"ABC": {...},
"ABC2": {...}
},
"Finance" : {
"Fin1":{....}
}
}
You can write a function that groups items by a single key.
function groupBySingleField(data, field){
return data.reduce((acc, val) => {
const rest = Object.keys(val).reduce((newObj, key) => {
if(key !== field){
newObj[key] = val[key]
}
return newObj;
}, {});
if (acc[val[field]]) {
acc[val[field]].push(rest);
} else {;
acc[val[field]] = [rest];
}
return acc;
}, {})
}
And then write a recursive function to additionally group already grouped items with a different key.
function groupByMultipleFields(data,...fields){
if(fields.length === 0 ) return;
let newData = {};
const [field] = fields;
newData = groupBySingleField(data, field);
const remainingFields = fields.slice(1);
if(remainingFields.length > 0){
Object.keys(newData).forEach((key) => {
newData[key] = groupByMultipleFields(newData[key],...remainingFields)
})
}
return newData;
}
You can try the code snippet here itself, to see if this is the result you are expecting
data = [
{
sector: "IT",
"company-name": "ABC",
contact: "Person1",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC",
contact: "Person2",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person1",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person2",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person3",
position: "Accountant"
},
{
sector: "Finance",
"company-name": "Fin1",
contact: "Person3",
position: "Accountant"
}
];
function groupBySingleField(data, field){
return data.reduce((acc, val) => {
const rest = Object.keys(val).reduce((newObj, key) => {
if(key !== field){
newObj[key] = val[key]
}
return newObj;
}, {});
if (acc[val[field]]) {
acc[val[field]].push(rest);
} else {;
acc[val[field]] = [rest];
}
return acc;
}, {})
}
console.log("Grouping by single fields");
console.log("Grouping by sector");
console.log(groupBySingleField(data, "sector"));
console.log("Grouping by company-name");
console.log(groupBySingleField(data, "company-name"));
function groupByMultipleFields(data,...fields){
if(fields.length === 0 ) return;
let newData = {};
const [field] = fields;
newData = groupBySingleField(data, field);
const remainingFields = fields.slice(1);
if(remainingFields.length > 0){
Object.keys(newData).forEach((key) => {
newData[key] = groupByMultipleFields(newData[key],...remainingFields)
})
}
return newData;
}
console.log("Grouping by multiple fields");
console.log("Grouping by company-name and position");
console.log(groupByMultipleFields(data,"company-name", "position"));
console.log("Grouping by position");
console.log(groupByMultipleFields(data,"position"));
console.log("Grouping by sector, company-name and position");
console.log(groupByMultipleFields(data,"sector", "company-name", "position"));
To get the output you're looking for, you could use something like this:
const result = {};
// loop through each object in data array
data.forEach(function(obj){
// create sector property if it doesn't exist
if (!result.hasOwnProperty(obj.sector)) result[obj.sector] = {};
// create company_name property within sector if it doesn't exist
if (!result[obj.sector].hasOwnProperty(obj.company_name)) result[obj.sector][obj.company_name] = {}
// set contact and position properties
result[obj.sector][obj.company_name]['contact'] = obj.contact;
result[obj.sector][obj.company_name]['position'] = obj.position;
});
console.log(result);
This code relies on each object in your data array having the same structure and properties. I'm not sure if you need your code to be more flexible.
Please check out the comments in the code to see exactly how it works.
Also, one important change is that you cannot use a hyphen in your property names. JS may interpret those as a minus sign when you're accessing properties. For that reason, you'll need to repace company-name with company_name in your data array for this code to work!