Dealing with Nested Objects in Javascript -- Not totally Understanding Concept - javascript

Still learning here. This one as simple as it seems, has beaten me up. I have managed to get the answer. But, when I do, I am getting more than one name coming out of my if statement. I used a new set to remove the duplicate names in the names array. But frankly, that seems lazy to me and I feel something can be done that is better.
Can someone show me some ways I'm missing to better go through this problem? I tried flattening and that didn't work either. Thank you for your help!
Here are the directions given to me:
Create a function passingStudents that accepts an array of student objects.
It should iterate through the list of students and return an array of the names of all the students who have an average grade of at least 70.
function passingStudents(students) {
const names = [];
students.forEach(student => {
student.grades.forEach(grade => {
if(grade.score >= 70) {
names.push(student.name);
}
});
});
let uniqueChars = [...new Set(names)];
return uniqueChars;
}
//Uncomment the lines below to test your function:
var students = [
{
"name": "Marco",
"id": 12345,
"grades": [{"id": 0, "score": 65}, {"id": 1, "score": 75}, {"id": 2, "score": 85}]
},
{
"name": "Donna",
"id": 55555,
"grades": [{"id": 0, "score": 100}, {"id": 1, "score": 100}, {"id": 2, "score": 100}]
},
{
"name": "Jukay",
"id": 94110,
"grades": [{"id": 0, "score": 65}, {"id": 1, "score": 60}, {"id": 2, "score": 65}]
}
];
console.log(passingStudents(students)); // => [ 'Marco', 'Donna' ]

The reason your solution introduces multiple people in the names array is that it adds the name for each grade they have that is at least 70. This means it will return a student if any of their grades is at least 70 rather than if their average grade is at least 70. While your solution may pass the test case provided, it will not work for every case in general. A better solution is to average the grades by using reduce and filter the original list of students based on whether their average is above 70. Then you can map the filtered students to their name.
function passingStudents(students) {
return students
.filter((student) => {
const { grades } = student;
const average = grades.reduce((sum, grade) => sum + grade.score, 0) / grades.length;
return average >= 70;
})
.map((student) => student.name);
}

Related

algorithm reorder list of objects

I am searching for an algorithm to reorder an list of dictionarys or in javascript array of objects.
For example I have the following list of objects:
my_dict = [
{
"id": 123,
"priority": 1
},
{
"id": 234,
"priority": 2
},
{
"id": 345,
"priority": 3
},
{
"id": 654,
"priority": 4
}
]
Now I want to change the item with id 654 to be the first priority. So the other items gets automatically to priority 2,3 and 4.
This should be the result:
my_dict = [
{
"id": 654,
"priority": 1
}
{
"id": 123,
"priority": 2
},
{
"id": 234,
"priority": 3
},
{
"id": 345,
"priority": 4
},
]
Another example is when I want to decrease the priority of item 234 to priority 3 then the item with priority 3 should become priority 2 (from the original/first dict). There are should no priority be twice in this list and there should not be gaps.
my_dict = [
{
"id": 123,
"priority": 1
},
{
"id": 345,
"priority": 2
},
{
"id": 234,
"priority": 3
},
{
"id": 654,
"priority": 4
}
]
For clarification here is an other example.
If I move item with id 123 to priority 4 the item before should become priority 1,2 and 3.
my_dict = [
{
"id": 234,
"priority": 1
},
{
"id": 345,
"priority": 2
},
{
"id": 654,
"priority": 3
}
{
"id": 123,
"priority": 4
},
]
The list should always start with priority 1. Can somebody explain how I can implement it with python or javascript? I tried it with a for loop starting and 1 but this does not work.
The discussions about a better data structure are spot-on. But if you still want to implement what you asked for, we can write a straightforward JS version that does the naive thing:
const changePriority = (xs, id, to,
idx = xs .findIndex (({id: i}) => i == id),
ys = [...xs .slice (0, idx), ...xs .slice (idx + 1)]
) =>
[ ...ys . slice (0, to - 1), xs [idx], ...ys .slice(to - 1)]
.map ((x, i) => ({...x, priority: i + 1}))
const dict = [{id: 123, priority: 1}, {id: 234, priority: 2}, {id: 345, priority: 3}, {id: 654, priority: 4}]
console .log (
changePriority (dict, 654, 2)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that this depends upon your id and the new priorty, to actually existing in the input. It wouldn't be hard to add error-checking.
Also note that this returns a new array, and doesn't mutate the original; we're not barbarians here.
I disagree with the idea of having priority be represented by both the position in the list and the "priority" member. This is redundant and will cause trouble at some point.
Further, I have to make assumptions to answer this question, as the question is not complete.
Assumptions:
In the end, the list should be sorted by priority again
Priorities should end up being integers
Priorities always start at 1 and enumerate the list without gaps
First approach
Specify exactly where you want to put your item by using halfs. This is simple and everything else should fall in place.
Example
Task: Move 234 to priority 3.
Sadly, it is not uniquely defined what this is supposed to mean, as it is unclear where the old item of priority 3 should move to.
Instead move it to priority 3.5 to make it unambiguous, then re-sort and re-enumerate:
# Change priority of '234' to '3.5'
my_dict[1]["priority"] = 3.5
# Sort
my_dict.sort(key=lambda x: x["priority"])
# Re-enumerate
for (position, item) in enumerate(my_dict):
item["priority"] = position + 1
print(my_dict)
[{'id': 123, 'priority': 1}, {'id': 345, 'priority': 2}, {'id': 234, 'priority': 3}, {'id': 654, 'priority': 4}]
For large datasets this is really slow though, as every single priority change is O(n*log(n)), as it requires a sort.
You should investigate in better data structures, like a heap or a linked list, depending on your exact usage scenario.
Second approach
If we remove the 'priority' member, we can write a much more efficient algorithm:
def move(data, original, target):
data.insert(target, data.pop(original))
# Important: Start priorities at 0, not 1. This makes this a lot easier.
my_dict = [123, 234, 345, 654]
move(my_dict, 3, 0)
print(my_dict)
my_dict = [123, 234, 345, 654]
move(my_dict, 1, 2)
print(my_dict)
[654, 123, 234, 345]
[123, 345, 234, 654]
https://ideone.com/o9UcCb
For a longer discussion about how to move an item inside of a list, read this discussion.

Javascript Function Output

I'm practicing how to maniupulate data in JS in this article: http://learnjsdata.com/combine_data.html
var articles = [
{"id": 1, "name": "vacuum cleaner", "weight": 9.9, "price": 89.9, "brand_id": 2},
{"id": 2, "name": "washing machine", "weight": 540, "price": 230, "brand_id": 1},
{"id": 3, "name": "hair dryer", "weight": 1.2, "price": 24.99, "brand_id": 2},
{"id": 4, "name": "super fast laptop", "weight": 400, "price": 899.9, "brand_id": 3}
];
var brands = [
{"id": 1, "name": "SuperKitchen"},
{"id": 2, "name": "HomeSweetHome"}
];
articles.forEach(function(article) {
var result = brands.filter(function(brand){
return brand.id === article.brand_id;
});
delete article.brand_id;
article.brand = (result[0] !== undefined) ? result[0].name : null;
});
I'm confused with the last part: article.brand = (result[0] !== undefined) ? result[0].name : null;
I understand the conditional operation: it wants to have null value if result[0] is not defined. But I'm wondering what result[0] refers to. I thought it would take first object: {"id":2, "name": "HomeSweetHome"} so there should be for loop to iterate all objects in order to see if objects meet the condition? Could you inform me what I'm missing or/and what result[0] refers to?
Thanks,
result[0] will be undefined in case there is no element in result. result is expected to be an array of brands filtered by the filter operation
The filtered array result will have same brand as that of the current article in the outer foreach loop. The filter condition is going to achieve that.
It looks like in this particular case you will get only one element in result array always as there are unique brand ids. It might have more elements in case of duplicated brand ids.
result[0] points to first element in the array result

Performance related issue for javascript object

I wanted to know if there is some performance issue related in the two below coding paradigms. Which one is the best and why?
var data = [{
"name": "ABC",
"code": 1,
"age": 97
},{
"name": "XYZ",
"code": 12,
"age": 12
},{
"name": "LMN",
"code": 121,
"age": 172
}
];
var response = [];
Method1
data.forEach(function(entry){
var obj = {
"NAME": entry["name"],
"AGE": entry["age"]
};
response.push(obj);
});
Method2
data.forEach(function(entry){
var obj = {};
obj["NAME"] = entry["name"];
obj["AGE"] = entry["age"];
response.push(obj);
});
For output object, I need say suppose, only 10 keys out of 100 keys
The object has many keys in it. Only limited are shown in the example. Which coding standard to choose and why? Please, can anybody explain?
No need to create the objects and then make a Array.prototype.push() for everly loop iteration...
You can directly use Array.prototype.map() instead of Array.prototype.forEach() and better access the object property values by the dot notation:
const data = [{
"name": "ABC",
"code": 1,
"age": 97
},{
"name": "XYZ",
"code": 12,
"age": 12
},{
"name": "LMN",
"code": 121,
"age": 172
}
];
const response = data.map(obj => ({
NAME: obj.name,
AGE: obj.age
}));
console.log(response)
Both approaches are fine and one should not be "faster" than the other, at least not enough to justify using the other one. Use the one you like.
But, if you're still looking for closure. The first one should be faster since it defines the entire object all at one go. There's no "look-ups" the engine have to do. It's all done at one go.
On a separate note, consider using the dot notation, it should be faster since the engine will not have to create strings. So, change
entry["name"]
to
entry.name
Also, if this is your actual code and the purpose is to modify the result to have just name and age and no code. You can do
data.forEach(user => delete user.code)

JavaScript search for object in an object and return both's elements

I've got an object like this:
{
"Peter": {
"data": {
"id": 104,
"budget": 3400
},
"items": {
"shoes": {
"sales": 72,
"intake": 576
},
"books": {
"sales": 14,
"intake": 70
}
}
},
"Bradley": {...}
}
The Peter object repeats several times with other names.
Now I want to search for lets say books.
If books is found somewhere in the object, it should return the elements in the found object books together with it's values.
I also want to know in which grandobject books was found. So in this case it should return Peter.
If books was found in several objects, it should return the results in an array.
I know how to get an object by searching for one of its properties by using the find() method, but I'm wondering how you do it when the objects you're searching through have names you don't know before.
Object.entries(), Array.prototype.filter(), Array.prototype.every(), and Object.values() can be combined to construct an array consisting only of people with predicated items.
See below for a practical example.
// Input.
const people = {"Peter": {"data": {"id": 104, "budget": 3400}, "items": {"shoes": {"sales": 72, "intake": 576}, "books": {"sales": 14, "intake": 70 }}}}
// People With.
const peopleWith = (o, ...p) => Object.entries(o).filter(([person, attributes]) => p.every(x => Object.values(attributes.items[x] || {}).length))
// Output.
const positive = peopleWith(people, 'books')
console.log('Found', positive) // [[person, attributes] ...]
const compound = peopleWith(people, 'books', 'shoes')
console.log('Compound', compound) // People with books and shoes.
const negative = peopleWith(people, 'books', 'shoes', 'hats')
console.log('None Found', negative) // []

Using $.grep to match array data to another array

Trying to see if data in one array matches the data in another. I have an array of objects, like so -
var ProductsList =
[
{"Name": "Product A"; "Rating": "3"},
{"Name": "Product B"; "Rating": "2"},
{"Name": "Product C"; "Rating": "1"},
];
I then want to compare this product list with user selected values, which come in an array that I get based on the values they selected via checkboxes. So if they selected 1, 2, 3 - all products should be shown, if they selected 1 - then only product A is shown.
I tried to use $.grep to do the filtering but I'm running into an issue filtering via array values. Let's hard code the user filer to all values as an example.
userFilterArray.Rating = [1, 2, 3];
function filter(ProductsList, userFilterArray)
filteredList = $.grep(ProductList, function(n) {
return (n.Rating == userFilterArray.Rating);
});
Obviously this doesn't work as I'm comparing n.Rating which is a string to an array but I'm not sure how to compare the string to string in this case.
Would grep be the easiest way to do this? Should I use a combo of .each .each? Maybe neither?
After a bunch of syntax and other fixes, I think this is what you're after:
var ProductsList = [
{"Name": "Product A", "Rating": 3},
{"Name": "Product B", "Rating": 2},
{"Name": "Product C", "Rating": 1}
];
var userFilterArray = [1, 3];
function filter(list, filterArr) {
return $.grep(list, function(obj) {
return $.inArray(obj.Rating, filterArr) !== -1;
});
}
var filteredList = filter(ProductsList, userFilterArray)
console.log( filteredList );
DEMO: http://jsfiddle.net/vK6N9/

Categories

Resources