Related
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);
I am new to javascript. I have an id, name and time that I am trying to get from my data and for each name I am trying to loop through the data and call a function from each name. Any help would be much appreciated. Thank you so much!
This is what I have done
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const getData = (id, name, time) => {
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.map(j => j.map(i => getData(id, i.name, i.time)))
Using a for...of loop, you can loop through your array and group on the keys of each object. Since you have arrays in your outer array, you can use another for loop to loop over those and get each object. Then you can use a for...in loop to over the keys in your object. For each key, you can check if it exists within grouped, and if it does, concatenate the object's value to the grouped array. If it doesn't exist, you can create a new element, and push the value. Once you have grouped everything, you can use destructuring to pull out the array values from your object into variables:
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = {};
for(const arr of data) {
for(const obj of arr) {
for(const key in obj) {
grouped[key] = (grouped[key] || []).concat(obj[key]);
}
}
}
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
The above concept can be achieved with .reduce() as well, where the grouped array gets built by using the accumulator argument of the reduce method, and each object is iterated using Object.entries() with .forEach():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = data.reduce((acc, arr) => {
arr.forEach(obj => {
Object.entries(obj).forEach(([key, val]) => {
acc[key] = [...(acc[key] || []), val];
});
})
return acc;
}, {});
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
Lastly, if you're happy with doing multiple iterations through your array, you can use .flatMap() to iterate through your array, and for each array, .map() over the objects inside of that. For each object you can extract the key using destructuring assignment. The result of the inner map is then flattened into the outer resulting array due to .flatMap():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const id = data.flatMap(arr => arr.map(({id}) => id));
const name = data.flatMap(arr => arr.map(({name}) => name));
const time = data.flatMap(arr => arr.map(({time}) => time));
console.log(id);
console.log(name);
console.log(time);
You could destructure the double nested array and take the entries of the object and push the values to the same named properties with 's'.
Ath the end destructure the objct for single arrays.
const
data = [[{ id: "14hyzdrdsquo", name: "Ronald", time: '12pm' }], [{ id: "1f496w43b8yi", name: "Jack", time: '1am' }]],
{ ids, names, times } = data.reduce((r, [o]) => {
Object.entries(o).forEach(([k, v]) => (r[k + 's'] ??= []).push(v));
return r;
}, {});
console.log(ids);
console.log(names);
console.log(times);
The problems is you put
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
inside getData function. That's mean you created new one arrays time when call getData
and for you solution better use .forEach instead of .map.
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
const getData = (id, name, time) => {
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.forEach(j => j.forEach(i => getData(id, i.name, i.time)))
console.log(ids)
console.log(names)
console.log(times)
You may do that using reduce inside each loop so you can have lower complexity.
const data = [
[
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
],
[
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
],
];
let ids = [];
let names = [];
let times = [];
data.forEach((elem) => {
const obj = elem.reduce(
(acc, curr, i) => {
acc.ids.push(curr.id);
acc.names.push(curr.name);
acc.times.push(curr.time);
return acc;
},
{
ids: [],
names: [],
times: [],
}
);
ids = [...ids, ...obj.ids];
names = [...names, ...obj.names];
times = [...times, ...obj.times];
});
console.log({
ids,
names,
times,
});
What I would like to do is to process JSON data and store each object after getting out of the for loop. However, the obj gets updated every iteration, so the objectArray holds only David's information in each element in it. I would like the objArray to hold each of the processed JSON objects (screenshot below). The JSON process is to store search a userId and name and store them in the objectArray. Could someone help me figure out how I could store each object in the objectArray? Thank you in advance.
const obj = {};
var objectArray = [];
var data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId" : "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
var dataLen = data.length;
var people = data;
createKeyValue = ((key, value) => {
var temp = {};
temp["value"] = value;
obj[key] = temp;
});
while (dataLen > 0) {
for (let [key, value] of Object.entries(data[0])) {
switch(key) {
case 'userId':
createKeyValue(key, value);
break;
case 'name':
createKeyValue(key, value);
break;
default:
}
}
objectArray.push(obj);
data.shift();
dataLen -= 1;
}
You can do this using a simple forEach() loop to create and push new objects to the objArray array.
const data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId": "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
];
let objArray = [];
data.forEach(person => {
objArray.push({
userId: { value: person.userId },
name: { value: person.name }
});
});
console.log(objArray);
The error you're seeing is because of a concept in JavaScript (and programming in general) known as "passing by reference."
Objects in JS, instead of being passed as whole groups of data, are passed around as addresses to where that data is stored. This saves a lot of overhead, since objects can become quite large.
In your case however, you're running into one of the ways it can trip you up. Since obj is really getting passed by reference instead of value, you're really .pushing 3 copies of the same address (of obj) onto objectArray rather than 3 distinct sets of data.
A better approach to this problem would be using a JS Array function called map(). This function is probably best explained by MDN:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
You can use it on your data array like this:
var objectArray = [];
var data = [{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId": "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
objectArray = data.map(dataEl => ({
userId: {
value: dataEl.userId,
},
name: {
value: dataEl.name,
},
}));
console.log(objectArray);
.as-console-wrapper {
max-height: 100% !important;
}
As said by our friends Kevin B and Zcoop98, its more appropriate to use forEach function, not map function:
data.forEach(elem => {
objectArray.push({
userId: { value: elem.userId },
name: { value: elem.name }
});
})
I'm having an issue returning the element which has been found in this hierarchical tree.
For example, if my selected item is:
{
"UID": 49,
"GUID": "",
"LocationName": "Doctor Smith's Office",
"LocationType": {
"UID": 2,
"LocationTypeName": "Practice",
"Description": "other location"
}
}
I will match up the UID to the below array of objects.
{
UID: 2,
GUID: "",
LocationName: "USA",
ParentLocation: null,
subs: [{
UID: 42,
GUID: "",
LocationName: "New Jersey",
Description: "",
subs: [{
UID: 3,
GUID: "",
LocationName: "Essex County",
ParentLocation: null,
"subs":[
UID: 4,
LocationName: "Newark",
ParentLocation: 3,
"subs": [
{
"UID": 49,
"GUID": "",
"LocationName": "Doctor Smith's Office",
"LocationType": {
"UID": 2,
"LocationTypeName": "Practice",
"Description": "other location"
},
"subs": [
{
"HostID": 38,
"HostName": "Ocean Host",
}
]
}
]
]
}
]
}]
};
let foundItem = this.findInTreeView(this.treeviewData[0], node.selectedNode);
// find selected node in treeview nav
// param: data - the treeview dataset
// param: selected - the selected node to be searched for in param 'data'
findInTreeView(data: any, selected: any ) {
let found;
if (this.foundInTree(data, selected)) {
return data;
}
let elem;
let ary = data.subs;
for (var i=0; i < ary.length; i++) {
elem = ary[i];
if (this.foundInTree(elem, selected)) {
// *** PROBLEM: If func has return true, I want to return the 'elem' object.
return elem;
}
}
for (var i=0; i < ary.length; i++) {
elem = ary[i];
if (elem.subs !== undefined) {
// recurse subs array
let found = this.findInTreeView(elem, selected);
if (found) {
return elem;
}
}
}
//return elem;
}
foundInTree(treeItem, node) {
if (treeItem.UID === node.UID) {
return true;
}
else {
return false;
}
}
It would be far easier to use a recursive reduce function, like this:
const input={UID:2,GUID:"",LocationName:"USA",ParentLocation:null,subs:[{UID:42,GUID:"",LocationName:"New Jersey",Description:"",subs:[{UID:3,GUID:"",LocationName:"Essex County",ParentLocation:null,"subs":[{UID:4,LocationName:"Newark",ParentLocation:3,"subs":[{"UID":49,"GUID":"","LocationName":"Doctor Smith's Office","LocationType":{"UID":2,"LocationTypeName":"Practice","Description":"other location"},"subs":[{"HostID":38,"HostName":"Ocean Host",}]}]}]}]}]};
const findUIDObj = (uid, parent) => {
const { UID, subs } = parent;
if (UID === uid) {
const { subs, ...rest } = parent;
return rest;
}
if (subs) return subs.reduce((found, child) => found || findUIDObj(uid, child), null);
};
console.log(findUIDObj(49, input))
You could use an explicit function which searches for the wanted UID.
function find(array, UID) {
var object;
array.some(o => {
if (o.UID === UID) {
return object = o;
}
return object = find(o.subs, UID);
});
return object;
}
var object = { UID: 2, GUID: "", LocationName: "USA", ParentLocation: null, subs: [{ UID: 42, GUID: "", LocationName: "New Jersey", Description: "", subs: [{ UID: 3, GUID: "", LocationName: "Essex County", ParentLocation: null, subs: [{ UID: 4, LocationName: "Newark", ParentLocation: 3, subs: [{ UID: 49, GUID: "", LocationName: "Doctor Smith's Office", LocationType: { UID: 2, LocationTypeName: "Practice", Description: "other location" }, subs: [{ HostID: 38, HostName: "Ocean Host", }] }] }] }] }] };
console.log(find([object], 49));
.as-console-wrapper { max-height: 100% !important; top: 0; }
One way to do this is to write a fairly generic version of a tree-finding function, and then configure it for your specific problem. Here we choose to test by matching on a supplied UID, we descend into children by looking at the subs property, and we convert the result by stripping out the subs property:
const searchTreeDF = (kids, test, convert, node) => test(node) // depth-first search
? convert(node)
: (kids(node) || []).reduce(
(found, child) => found || searchTreeDF(kids, test, convert, child),
false
)
const subs = node => node.subs
const matchId = (uid) => (item) => item.UID === uid
const convert = ({subs, ...rest}) => ({...rest})
const findUid = (uid, tree) => searchTreeDF(subs, matchId(uid), convert, tree)
// ...
const tree = {"GUID": "", "LocationName": "USA", "ParentLocation": null, "UID": 2, "subs": [{"Description": "", "GUID": "", "LocationName": "New Jersey", "UID": 42, "subs": [{"GUID": "", "LocationName": "Essex County", "ParentLocation": null, "UID": 3, "subs": [{"LocationName": "Newark", "ParentLocation": 3, "UID": 4, "subs": [{"GUID": "", "LocationName": "Doctor Smith's Office", "LocationType": {"Description": "other location", "LocationTypeName": "Practice", "UID": 2}, "UID": 49, "subs": [{"HostID": 38, "HostName": "Ocean Host"}]}]}]}]}]}
console.log(findUid(49, tree))
But if we didn't want to pass in the UID directly, but instead wanted to pass in an element that has its own UID property, we could write
const matchElem = (elem) => (item) => elem.UID === item.UID
and then do this:
const findUid2 = (elem, tree) => searchTreeDF(subs, matchElem(elem), convert, tree)
// ...
findUid2({UID: 49}, tree)
Or if we wanted to not convert the result, and keep the subs property, we could just supply an identity function for convert:
const findUid = (uid, tree) => searchTreeDF(subs, matchId(uid), x => x, tree)
Or we could mix and match as we please. Also note that the configuration does not have to use named functions. We could just as easily write
const findUid = (uid, tree) => searchTreeDF(
node => node.subs || [],
(item) => item.UID === uid,
({subs, ...rest}) => ({...rest}),
tree
)
Generic functions are not always the right answer. But they can help separate out those things that change from the more basic algorithm we're writing. I think in this case it helps make things more maintainable.
Got an object containing a user id for each user and prices, would like to create a new object/array for each user (no duplicates) and be able to calculate the total sum of price for each user. Tried using Object.values() with map and filter but can't get it to work properly
{
"data": {
"item1": {
"price": "20",
"user": "user1"
},
"item2": {
"price": "10",
"user": "user2"
},
"item3": {
"price": "50",
"user": "user1"
}
}
}
Output something like this:
{
"users": {
"user1": {
"totalSum": "70",
},
"user2": {
"totalSum": "10",
}
}
}
I'm thinking about using map to present the "users"-data, maybe an array would be better?
Using function reduce.
Important: The attribute price is a String, this approach uses object Number to convert that value to a numeric one.
var obj = { "data": { "item1": { "price": "20", "user": "user1" }, "item2": { "price": "10", "user": "user2" }, "item3": { "price": "50", "user": "user1" } }};
var result = Object.keys(obj.data).reduce((a, k) => {
if (a.users[obj.data[k].user]) {
a.users[obj.data[k].user].totalSum += Number(obj.data[k].price);
} else {
a.users[obj.data[k].user] = {
"totalSum": Number(obj.data[k].price)
}
}
return a;
}, {
'users': {}
});
console.log(result);
.as-console-wrapper {
max-height: 100% !important; top: 0;
}
You could leverage ```reduce, more information here
code (haven't tried this)
var data = JSON.parse(mainObj).data;
var usersWithTotalExpenditure = Object.keys(data).reduce(function(result, key) {
var currentItem = data[key];
var useName = currentItem.user;
var price = Number(currentItem.price);
if (userName in result) {
result[userName].totalSum += price;
} else {
result[userName] = {
totalSum: price
};
}
return result;
}, {});
var resultObject = {
users: usersWithTotalExpenditure
}
You can use a forEach loop. This relies on Javascripts powerful OR operator, which coerces the first half of the expression to false if the current user's price is not defined (meaning it is a user the loop hasn't encountered before)
`c is your initial object's data, output is empty object`
const c = obj.data;
var output = {};
Object.keys(c).forEach((val) => {
output[c[val]["user"]] = parseInt(output[c[val]["user"]]) + parseInt(c[val]["price"]) || parseInt(c[val]["price"]);
})