So I have a series of 4 JSON objects with nested data inside each of them. Each of these objects are stored in an array called classes. Here is an example of how one of the class objects is formatted:
let class_A = {
professor: "Joey Smith",
numberStudents: 25,
courseCode: "COMS 2360",
seating: {
"FirstRow": {
0: {
firstName: "Sarah",
collegeMajor: "English",
},
1: {
firstName: "Bob",
collegeMajor: "Computer Engineering",
},
2: {
firstName: "Dylan",
collegeMajor: "Mathematics",
}
},
"SecondRow": {
3: {
firstName: "Molly",
collegeMajor: "Music"
}
}
}
};
I'm struggling to figure out how to access the very last fields within each class object (firstName and collegeMajor). The furthest I was able to get was the indexes beneath each row number.
let classes = [class_A, class_B, class_C, class_D];
let classesAvailable = document.getElementById('classes');
let class = classes[classesAvailable.value];
for(rowNum in class.seating){
for(index in class.seating[rowNum]){
console.log(index);
//console.log(class.seating[rowNum[index]].firstName);
}
}
So in this example, console.log(index) prints out:
0
1
2
3
but I'm unable to print the first name and college major of each student in each row. I was trying to follow a similar logic and do console.log(class.seating[rowNum[index]].firstName) but I get the error:
Cannot read properties of undefined (reading 'firstName')
Was wondering if anyone knows what's wrong with my logic here?
console.log(class.seating[rowNum][index])
Related
I have array that contains plenty of data. Format is always like that:
1:
UserName: "John Smith"
Priority: "2"
Time Occured: "02/09/2019 11:20:23"
Time Ended: "02/09/2019 11:20:23"
2:
UserName: "Tom Bill"
Priority: "4"
Time Occured: "01/08/2019 13:20:23"
Time Ended: "04/08/2019 15:20:23"
3:
UserName: "John Smith"
Priority: "2"
Time Occured: "06/08/2019 13:20:23"
Time Ended: "09/09/2019 15:20:23"
...
Of course there is more stuff, but just to give you idea of structure.
Array contains entries that might be under the same user name. As user can have multiple entries
What I want to do, is sort and modify it to the way I can use it on data table. I am not sure what approach might be the best or what is possible.
I was thinking that I need to modify array do some math in meantime. So in Data table I can present that John Smith, got 8 entries, two of them are sev 4 etc etc. Tom Bill got 4 entries etc. Basically I won't use original data as I need to modify some parts of it, for Example I am not interested in date itself, but if it was in the past or in the future, already got scripts for that, yet I need to do it for every single user.
A structure something like this seems to be sufficient for your requirement:
data = {
'John Smith' : [{ Priority : 1, .... }, { ...2nd instance }],
'John Doe' : [{...1st instance of John Doe}],
}
Basically an object that has the names for keys, and each key has an array of entries of data.
Whenever you wish to add more entries to John Smith, you get access to the array directly by using data['John Smith']
EDIT
To convert the data to this format.
data = [
{
'UserName': "John Smith",
'Priority': "2",
'Time Occured': "02/09/2019 11:20:23",
'Time Ended': "02/09/2019 11:20:23",
},
{
'UserName': "Tom Bill",
'Priority': "4",
'Time Occured': "01/08/2019 13:20:23",
'Time Ended': "04/08/2019 15:20:23",
},
{
'UserName': "John Smith",
'Priority': "2",
'Time Occured': "06/08/2019 13:20:23",
'Time Ended': "09/09/2019 15:20:23",
}
]
convertData = (data) =>{
let newData = {}
for(let i = 0; i<data.length; i++){
// console.log(data[i])
let name = data[i]['UserName']
tempData = {
'Priority' : data[i]['Priority'],
'Time Occured' : data[i]['Time Occured'],
//Add more properties here
}
if (newData[name]==null){
newData[name] = []
}
newData[name] = [...newData[name], tempData]
}
console.log(newData)
}
convertData(data)
Look at this codepen.
https://codepen.io/nabeelmehmood/pen/jONGQmX
Wanted to know how can we access infinitely nested object in JS?
Consider this example given to be my interviewer
You have an object with a Parent and infinitely nested children
[
{
name: "Jack",
age: "98" ,
profession: "doctor
children: [
{
name: "Varun",
age: "80"
profession: "scientist"
children: [
{
name: "Ishan"
age: "62",
profession: "teacher
children: [{....
.....
.....[{
name: "Rahul",
age: "23",
profession: "engineer"
children: [{
.....
I need to find the name corresponding where profession is "engineer" and how deep that given object is nested.
Note: Number of children to be considered here is infinite.
Question: Can someone help me in figuring out how I can do it with recursion and without recursion
update: He gave me a hint to use divide and conquer
Update: Based on the solution by Bary, I tried something like this
let infiniteArray = [
{
name: "Jack",
age: "98" ,
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
]
function isTheItem(item) {
if (item["profession"] === "teacher") return item
return false
}
function walk(collection) {
return collection.find(item => isTheItem(item) || walk(item.children));
}
But when I console.log it, i.e console.log(walk(infiniteArray)), it is logging the entire infiniteArray and I understand why but I went it to return just the name corresponding to profession. Any help on how I can achieve it?
name: "Ishan",
age: "62",
profession: "teacher"
}
There's two directions walking through your collection
* sibling
* nested
so you can iterate this sibling items with regular array iterator and recursively walk through the children of each item.
function isTheItem(item) {
return ...;
}
function walk(collection) {
return collection.find(item => isTheItem(item) || walk(item.children));
}
Divide and conquer algorithms usually have more to do with sorting values in an array (during an interview process). If you were sorting by age or something, I could see that being a hint. But there's no way to really divide and conquer when searching for a key value pair of an infinite number of nested objects. You have to search through n number of values until you find the result. Any dividing of the object doesn't necessarily improve efficiency with a random key value pair stored that you want to find.
More experienced devs correct me if I'm wrong here
Barry Gane has a good start to the code you would write for the actual application of this.
This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 4 years ago.
So I have a variable called elements that hold a bunch of data. Let's say, for example, that this is it:
{
name: "Jeff",
age: "18",
carList: [
"car1": "...",
"car2": "..."
],
surname: "Matt"
}
I also have an empty array, at the start, declared this way:
public myArray: Model[] = [];
Model is just a class that contains the definition of the elements.
What I'm trying to achive is to have a copy of elements without keeping any reference to the original variable. but I'm missing something. Here's a pseudo-code that do the trick (almost) :
myfunction(){
//Do stuff that change `car1` from `...` to `hello`
myArray.push(Object.assign({}, this.elements));
}
And finnally a I have a function that print everything in the myArray variable:
function print(){
this.myArray.forEach(el => {
console.log(el);
});
}
The problem is that every element of the array has the latest update done by me.
So if myFunction was called several time doing something like:
Change "Jeff" to "Jack"
Change age "18" to "25"
Change surname to "Seth"
what I see is three log showing this data:
{
name: "Jeff",
age: "25",
carList: [
"car1": "...",
"car2": "..."
],
surname: "Seth"
}
My question is: Why even using Object.asign it still keeping the reference to it's original object? I should keep the history of each modification, instead of having just the last edit in both of three edits I've made.
Thank you.
Try the following:
JSON.parse(JSON.stringify(element));
It keeps no refrences, as you can observe in the snippet below:
let element = { name: "Jeff", age: "18", carList: [ {"car1": "x"}, {"car2": "y"} ], surname: "Matt" };
let result = JSON.parse(JSON.stringify(element));
result.name = "x";
console.log(result);
console.log("****element*****");
console.log(element);
I have 100 documents in my mongoDB, assuming each of them are possible duplicate with other document(s) in different conditions, such as firstName & lastName, email and mobile phone.
I am trying to mapReduce these 100 documents to have the key-value pairs, like grouping.
Everything works fine until I have the 101st duplicate records in the DB.
The output of the mapReduce result for the other documents which are duplicate with the 101st records are corrupted.
For example:
I am working on firstName & lastName now.
When the DB contains 100 documents, I can have the result containing
{
_id: {
firstName: "foo",
lastName: "bar,
},
value: {
count: 20
duplicate: [{
id: ObjectId("/*an object id*/"),
fullName: "foo bar",
DOB: ISODate("2000-01-01T00:00:00.000Z")
},{
id: ObjectId("/*another object id*/"),
fullName: "foo bar",
DOB: ISODate("2000-01-02T00:00:00.000Z")
},...]
},
}
It is what exactly I want, but...
when the DB contains more than 100 possible duplicated documents, the result became like this,
Let's say the 101st documents is
{
firstName: "foo",
lastName: "bar",
email: "foo#bar.com",
mobile: "019894793"
}
containing 101 documents:
{
_id: {
firstName: "foo",
lastName: "bar,
},
value: {
count: 21
duplicate: [{
id: undefined,
fullName: undefined,
DOB: undefined
},{
id: ObjectId("/*another object id*/"),
fullName: "foo bar",
DOB: ISODate("2000-01-02T00:00:00.000Z")
}]
},
}
containing 102 documents:
{
_id: {
firstName: "foo",
lastName: "bar,
},
value: {
count: 22
duplicate: [{
id: undefined,
fullName: undefined,
DOB: undefined
},{
id: undefined,
fullName: undefined,
DOB: undefined
}]
},
}
I found another topic on stackoverflow having the similar issue like me, but the answer does not work for me
MapReduce results seem limited to 100?
Any ideas?
Edit:
Original source code:
var map = function () {
var value = {
count: 1,
userId: this._id
};
emit({lastName: this.lastName, firstName: this.firstName}, value);
};
var reduce = function (key, values) {
var reducedObj = {
count: 0,
userIds: []
};
values.forEach(function (value) {
reducedObj.count += value.count;
reducedObj.userIds.push(value.userId);
});
return reducedObj;
};
Source code now:
var map = function () {
var value = {
count: 1,
users: [this]
};
emit({lastName: this.lastName, firstName: this.firstName}, value);
};
var reduce = function (key, values) {
var reducedObj = {
count: 0,
users: []
};
values.forEach(function (value) {
reducedObj.count += value.count;
reducedObj.users = reducedObj.users.concat(values.users); // or using the forEach method
// value.users.forEach(function (user) {
// reducedObj.users.push(user);
// });
});
return reducedObj;
};
I don't understand why it would fail as I was also pushing a value (userId) to reducedObj.userIds.
Are there some problems about the value that I emitted in map function?
Explaining the problem
This is a common mapReduce trap, but clearly part of the problem you have here is that the questions you are finding don't have answers that explain this clearly or even properly. So an answer is justified here.
The point in the documentation that is often missed or at least misunderstood is here in the documentation:
MongoDB can invoke the reduce function more than once for the same key. In this case, the previous output from the reduce function for that key will become one of the input values to the next reduce function invocation for that key.
And adding to that just a little later down the page:
the type of the return object must be identical to the type of the value emitted by the map function.
What this means in the context of your question is that at a certain point there are "too many" duplicate key values being passed in for a reduce stage to act on this in one single pass as it will be able to do for a lower number of documents. By design the reduce method is called multiple times, often taking the "output" from data that is already reduced as part of it's "input" for yet another pass.
This is how mapReduce is designed to handle very large datasets, by processing everything in "chunks" until it finally "reduces" down to a singular grouped result per key. This is why the next statement is important is that what comes out of both emit and the reduce output needs to be structured exactly the same in order for the reduce code to handle it correctly.
Solving the problem
You correct this by fixing up how you are both emitting the data in the map and how you also return and process in the reduce function:
db.collection.mapReduce(
function() {
emit(
{ "firstName": this.firstName, "lastName": this.lastName },
{ "count": 1, "duplicate": [this] } // Note [this]
)
},
function(key,values) {
var reduced = { "count": 0, "duplicate": [] };
values.forEach(function(value) {
reduced.count += value.count;
value.duplicate.forEach(function(duplicate) {
reduced.duplicate.push(duplicate);
});
});
return reduced;
},
{
"out": { "inline": 1 },
}
)
The key points can be seen in both the content to emit and the first line of the reduce function. Essentially these present a structure that is the same. In the case of the emit it does not matter that the array being produced only has a singular element, but you send it that way anyhow. Side by side:
{ "count": 1, "duplicate": [this] } // Note [this]
// Same as
var reduced = { "count": 0, "duplicate": [] };
That also means that the remainder of the reduce function will always assume that the "duplicate" content is in fact an array, because that is how it came as original input and is also how it will be returned:
values.forEach(function(value) {
reduced.count += value.count;
value.duplicate.forEach(function(duplicate) {
reduced.duplicate.push(duplicate);
});
});
return reduced;
Alternate Solution
The other reason for an answer is that considering the output you are expecting, this would in fact be much better suited to the aggregation framework. It's going to do this a lot faster than mapReduce can, and is even far more simple to code up:
db.collection.aggregate([
{ "$group": {
"_id": { "firstName": "$firstName", "lastName": "$lastName" },
"duplicate": { "$push": "$$ROOT" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } }}
])
That's all it is. You can write out to a collection by adding an $out stage to this where required. But basically either mapReduce or aggregate, you are still placing the same 16MB restriction on the document size by adding your "duplicate" items into an array.
Also note that you can simply do something that mapReduce cannot here, and just "omit" any items that are not in fact a "duplicate" from the results. The mapReduce method cannot do this without first producing output to a collection and then "filtering" the results in a separate query.
That core documentation itself quotes:
NOTE
For most aggregation operations, the Aggregation Pipeline provides better performance and more coherent interface. However, map-reduce operations provide some flexibility that is not presently available in the aggregation pipeline.
So it's really a case of weighing up which is better suited to the problem at hand.
I want to create a multi-level JSON string with JS.
Scenario
3 countries with 5 grandfathers with 3 kids which whom also have 3 kids that have 5 friends.
I get the data from a external JSON file that looks like this.
{"countries":[
{
"name":"USA",
"grandfathers":[
{
"gFName":"Steve",
"grandfathersKid":[
{
"gFKName": "Linda",
"kid": [{
"name": "Steve JR",
"friends": [{
"name": "Kriss|John|Martin|Steven"
}]
}
]
}
]
}
]
}
]}
And now I want to store some of the countries with people and their relatives and friends in a a new JSON list that looks exactly as the list made in the external json file. I aim to use this "homemade" list later on in the script.
My initial response for this was
var tree = new Array();
tree = {};
var countries = new Array();
countries[0] = "canada";
countries[1] = "USA";
countries[2] = "Mexico";
countries[0][0] = "Steve"; //Lives in Canada
countries[0][0][0] = "Linda"; //Daughter of Steve
countries[0][0][0][0] = "Steve JR"; // Kid of Linda
countries[0][0][0][0][0] = "Kriss"; //Steves Friend
...
$.each(countries...function(index, value){
tree[index].country = value;
$.each(grandfathers...function(key, value){
tree[index].country[key].grandfather = value;
}
And so on, but this is not giving me the result I want. What am I doing wrong? And a more effective way than to take each of everything?
Third edit...
Is this the sort of thing you're trying to do?
var countries = $.map(oldCountries || [], function(country) {
return {
name: country.name,
people: $.map(country.grandfathers || [], function(gpa) {
return {
name: gpa.gFName,
children: $.map(gpa.grandfathersKid || [], function(parent) {
return {
name: parent.gFKName,
children: $.map(parent.kid || [], function(kid) {
return {
name: kid.name,
friends: kid.friends
};
})
};
})
};
})
};
});
I wasn't sure what to do with the friends node. Should that be normalized into something more useful, or do you want to leave it alone?
This Fiddle demonstrates the technique.
I think we'd need to know more about your requirements. But several thing I see here are:
You declare tree and initialize it as an Array, then immediately reinitialize it as an
empty object
You are not creating the intermediate nodes here, such as tree[index] but just assuming
that they exist.
You are trying to assign the country[key] property of an object, using the dot-property
access.
Can you supply the countries structure and the grandfather's structure. And are they nested?
And finally, what would you like for the output format? The code above hints at it, but it's still a little fuzzy.
Edit
So are you trying to achieve a structure something like this?:
var countries = [
{
name: "Canada",
people: [
{
name: "Steve",
children: [
{
name: "Linda",
children: [
{
name: "Steve, Jr.",
friends: [
{
name: "Kriss"
}
//, more friends
]
}
//, more grandchildren
]
}
//, more parents
]
}
//, more grandparents
]
}
//, more countries
];
May be this jsfiddle can help you to get started?
And here is an example derived from your code.
Sounds like a homework, so I'll try to point you in the right direction. I think you are confusing objects and arrays. You could use a "country" object and a "person" object. A country object should have an array of person objects, as inhabitants. Person objects can have an array of person objects as descendants. Add a method like "addDescendant", which creates a new person under a person. From There you can build the structure as you like. Here is some pseudo code:
countries = [];
function Country(name){ this.name = name; this.population = [];}
function Person(kids){this.descendants = []; this.addDescendant = function(){...};
//loop from 1 to kids and add descendants as "new Person"
}
person = new Person(3);
country1 = new Country("MyCountry1");
// now add people to country1.population
countries.push(country1);
The final structure should look something like this:
countries = [
{ name: "country 1",
people: [{ name: "Steve"},
{name: "Clara", descendants: [{name: "Clara's daughter"},
{name: "Clara's son"}]
]}
},
{ name: "country 2",
people: [{}, {} ...]
}
];