JavaScript Deeply Nested Array filtering - javascript

I have created a somehow functional "deeply" nested array filtering functionality. This functionality displays car, which has a color "RED" assigned to them. However the cars, which "pass" the filter is the car, which only has 1 color assigned. How can ensure that arrays within arrays are being checked by the criteria? Would I require creating a loop within loop? The desired outcome would be that if criteria is set to "RED" it will also display the BMW and SUZUKI as these cars also have this color. Any help, suggestions or further links to other posts to achieve the desired outcome would be truly appreciated as I was not able to find anything very useful on my own. The post, which has been most useful has been this one - Filtering an array with a deeply nested array in JS Below I have attached the code snippet of my current code.
const myCars = [
{ name: "BMW",colour: ["White","Red","Black"] },
{ name: "AUDI",colour: ["Yellow","Silver"] },
{ name: "VW",colour: ["Purple","Gold"] },
{ name: "NISSAN",colour: ["White","Black"] },
{ name: "SUZUKI",colour: ["Red"] },
];
for (x in myCars) {
var keys = Object.keys(myCars[x])
var carSpec = myCars.filter(function(fltr) {
return (fltr.colour == "Red");
});
document.getElementById("show1").innerHTML += carSpec[x].name + " " + "has these colours - " + carSpec[x].colour + "<hr />";
}
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>

You need to filter by an includes test over the colour array:
const myCars = [
{ name: "BMW",colour: ["White","Red","Black"] },
{ name: "AUDI",colour: ["Yellow","Silver"] },
{ name: "VW",colour: ["Purple","Gold"] },
{ name: "NISSAN",colour: ["White","Black"] },
{ name: "SUZUKI",colour: ["Red"] },
];
const show1 = document.getElementById("show1");
myCars
.filter(({ colour }) => colour.includes('Red'))
.forEach(({ name, colour }) => {
show1.innerHTML += name + " " + "has these colours - " + colour + "<hr />";
});
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>

You can use reduce to get an array of cars which have are available in red , then use forEach to loop over it and display the text.
join will join all the contents of an array by the delimiter
const myCars = [{
name: "BMW",
colour: ["White", "Red", "Black"]
},
{
name: "AUDI",
colour: ["Yellow", "Silver"]
},
{
name: "VW",
colour: ["Purple", "Gold"]
},
{
name: "NISSAN",
colour: ["White", "Black"]
},
{
name: "SUZUKI",
colour: ["Red"]
},
];
let hasRed = myCars.reduce(function(acc, curr) {
if (curr.colour.indexOf('Red') !== -1) {
acc.push(curr)
}
return acc;
}, []).forEach((item) => {
document.getElementById("show1").innerHTML += item.name + " " + "has these colours - " + item.colour.join(',') + "<hr />";
})
<p>Car List.</p>
<p id="show"></p>
<p id="show1"></p>

Related

How do I incorporate the index of an array while defining a property of said array within a class in JavaScript?

Sorry if the title makes no sense.. let me explain
Say I have the following 2d array.. the first array representing ice cream and the second representing milkshakes
menu = [ ['vanilla', 'chocolate', 'almond'],
['vanilla', 'pineapple', 'strawberry'] ]
Now I create a class that takes this array as input
class cafe{
constructor(menu){
this.iceCreams = menu[0]
this.milkshakes = menu[1]
}
}
Now I want to define a property called 'price' for each flavor of milkshake.
this.milkshakes[n].price = < a function that computes price based on value of n >
so that i can access them like this :
cafe.milkshakes[0].price
So how do I incorporate the index 'n' of the array while defining the property
I haven't tried anything bcos I dont know how to even approach this ☹️
You can do it in your constructor.
You can grab the names, and call map function on it and do whatever you want. Please check the following example. There, calculatePrice is a function that takes the index and returns the price based on the index.
class Cafe {
constructor (menu) {
this.iceCreams = menu[0].map((flavor, index) => {
return {
flavor,
price: calculatePrice(index)
}
});
this.milkshakes = menu[1].map((flavor, index) => {
return {
flavor,
price: calculatePrice(index)
}
});
}
This is a minimal answer.
UPDATE:
For a detailed and improved answer: https://codesandbox.io/s/cafe-example-wxp2c4
So, in the milkshakes array you need each item as an object data structure, not a string.
menu = [ ['vanilla', 'chocolate', 'almond'],
[{ flavor: 'vanilla' }, { flavor: 'pineapple' }, { flavor: 'strawberry' }] ]
and then you can loop through and set the price, something like this.
menu.milkshakes.forEach((item, index) => item.price = index))
you can use objects:
menu = [
[
{
name: "vanilla",
price: 200,
},
{
name: "chocolate",
price: 200,
},
{
name: "almond",
price: 200,
},
],
[
{
name: "vanilla",
price: 200,
},
{
name: "pineapple",
price: 200,
},
{
name: "strawberry",
price: 200,
},
],
];
and then:
class cafe{
constructor(menu){
this.iceCreams = menu[0]
this.milkshakes = menu[1]
}
}
now iceCreams and milshakes have the property price and name
example:
iceCreams[n].price
iceCreams[n].name

Hi i am having a problem in java script with function and arrays

Print a shopping list
Create a function called print Shopping List that take a list of products and print the items to the screen.
//
shopping List = [
{ item Name : 'Bread', price : 11.00 },
{ item Name : 'Milk', price : 7.00 },
{ item Name : 'Cheese', price : 19.50 }
];
This should print:
Shopping list:
* Bread # R11.00
* Milk # R7.00
* Cheese # R19.50
console.log('Shopping List:');
shoppingList.forEach(ele => {
console.log('*'+ele.ItemName+'#'+'R'+ele.price);
);
Hope this helps.
You can try the below code. Hope it helps.
const shoppingList = [{
itemName: 'Bread',
price: 11.00
},
{
itemName: 'Milk',
price: 7.00
},
{
itemName: 'Cheese',
price: 19.50
}
];
const itemsHTML = shoppingList.map(item => (
`<li>${item.itemName} # R${item.price}</li>`
))
document.write(`<ul>${itemsHTML}</ul>`);
You can simply achieve it by creating a node in the HTML DOM and create the custom string by using template string.
Demo :
const shoppingList = [
{ itemName : 'Bread', price : 11.00 },
{ itemName : 'Milk', price : 7.00 },
{ itemName : 'Cheese', price : 19.50 }
];
shoppingList.forEach(item => {
const node = document.createElement("li");
const liNode = document.getElementById('priceList').appendChild(node);
liNode.innerHTML = `${item.itemName} # R${item.price}`
});
<ul id="priceList"></ul>
Your syntax is wrong in a lot of ways. An identifier (variable name, function name, etc. — “identifier” is programming jargon for “name”) cannot contain a space in almost all programming/scripting languages. So, shopping list should be shoppingList (or shopping_list), item name should be itemName or, even better, just name. As you see in the forEach part, naming the running position item, I access the name using item.name.
const shoppingList = [
{ name: 'Bread', price: 11.00 },
{ name: 'Milk', price: 7.00 },
{ name: 'Cheese', price: 19.40 },
];
shoppingList.forEach(item => {
console.log(item.name + " # R" + item.price.toFixed(2));
})

Mapping through array of objects after filtering and displaying dynamic common property as a header

I have an array of objects:
const students = [
{ name: 'Tom', class: "Blue" },
{ name: 'Pom', class: "Blue" },
{ name: 'Max', class: "Red" },
{ name: 'Alex', class: "Red" },
{ name: 'John', class: "Yellow" }
];
And I would like to group the return values by the class property, so I can achieve something similar to this in HTML:
Class Blue: Tom, Pom
Class Red: Max, Alex
Class Yellow: John
note: the class property should be displayed once as HTML mark-up which is the reason I don't think this helps at all..
How should I go about it? I can user filter() (like below) but this is rather silly. I would ideally like to avoid doing this several times especially given the class property is dynamic (not known before hand).
const classRed = students.filter(student => student.class === "Red);
const classBlue = students.filter(student => student.class === "Blue);
...
I started this jsfiddle, but not sure how to deal with this object to display how I want it.
I guess this question is about getting the right data structure so I can then map through it to display what I need, rather than simply sorting the array.
Use reduce method to do the group by operation and use Object.entries to get the list of items to iterate.
Try like this
{
Object.entries(
students.reduce((prevValue, currValue) => {
prevValue[currValue.class]
? prevValue[currValue.class].push(currValue.name)
: (prevValue[currValue.class] = [currValue.name]);
return prevValue;
}, {})
).map(([classs, names]) => `Class ${classs}: ${names.join(", ")}`);
}
function App({}) {
const students = [
{ name: "Tom", class: "Blue" },
{ name: "Pom", class: "Blue" },
{ name: "Max", class: "Red" },
{ name: "Alex", class: "Red" },
{ name: "John", class: "Yellow" }
];
return (
<div>
{Object.entries(
students.reduce((prevValue, currValue) => {
prevValue[currValue.class]
? prevValue[currValue.class].push(currValue.name)
: (prevValue[currValue.class] = [currValue.name]);
return prevValue;
}, {})
).map(([classs, names]) => (
<div>{`Class ${classs}: ${names.join(", ")}`}</div>
))}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
JS Fiddle => https://jsfiddle.net/9ahtz2jd/11/
NOTE: Always try to use filter, map, reduce functions.
You could create a new object with the name of the classes as keys and an array with the names of the students as values.
const students = [
{ name: 'Tom', class: "Blue" },
{ name: 'Pom', class: "Blue" },
{ name: 'Max', class: "Red" },
{ name: 'Alex', class: "Red" },
{ name: 'John', class: "Yellow" }
];
let klasses = {};
for (let i = 0; i < students.length; i++) {
let student = students[i];
let klass = student.class;
if (!klasses[klass]) {
klasses[klass] = [student.name];
} else {
klasses[klass].push(student.name);
}
}
console.log(klasses);
let classes = {};
for(let student of students) {
classes[student.class] = [...classes[student.class] || [], student.name];
}
console.log(classes);

How to properly check if a name is available in an array made of objects (record collection)?

so my plan for this was to have a message appear asking someone to type in a student name. Javascript would look through a record, which is in a seperate JS file, and then output that in the message variable. If suppose the student didn't exist, the output message would be the alert box in the else statement.
Heres a record of the students:
var students=[
{
name:'Chris',
track:'IOS',
achievements:'100',
points:'1000'
},
{
name:'John',
track:'Web Design',
achievements:'90',
points:'1000'
},
{
name:'Brent',
track:'Front-End',
achievements:'70',
points:'1000'
},
{
name:'Josh',
track:'Full-Stack',
achievements:80,
points:'1000'
},
{
name:'Nick',
track:'AI',
achievements:'60',
points:'1000'
}
];
var message="";
var search=prompt("Type name of student");
while (search!=="quit") {
for (var i=0; i<students.length; i+=1) {
var studentName=students[i].name;
if (studentName===search) {
message+="<h1>"+studentName+"</h1>";
message+="<p>"+student[i].track+"</p>";
message+="<p>"+student[i].achievements+"</p>";
message+="<p>"+student[i].points+"</p>";
break;
} else {
alert("That student does not exist. Try again");
break;
}
}
search=prompt("Type name of student");
}
print(message);
When I try this code, it asks me for the student's name and then says he/she is not available. Apparently, the determination that the student is not in the list should only be made after the loop has finished checking all the students. Then, and only if nothing was found, should the failure message be output.
The problem for me, conceptually, is that the final value of the variable, studentName, after the for loop ends will be the name property of the last object in the array. So how would I redesign my for loop then?
How can I redesign my code to accomplish just that?
You can try this,
var message="";
var search=prompt("Type name of student");
while (search!=="quit") {
// we will get result if any one student name matches
var result = students.find((student) => student.name === search);
if (result) {
message+="<h1>"+result.name+"</h1>";
message+="<p>"+result.track+"</p>";
message+="<p>"+result.achievements+"</p>";
message+="<p>"+result.points+"</p>";
}
else {
alert("That student does not exist. Try again");
}
search=prompt("Type name of student");
}
print(message);
you can filter your list first and then check it like
const students = [
{
name: 'Chris',
track: 'IOS',
achievements: '100',
points: '1000'
},
{
name: 'John',
track: 'Web Design',
achievements: '90',
points: '1000'
},
{
name: 'Brent',
track: 'Front-End',
achievements: '70',
points: '1000'
},
{
name: 'Josh',
track: 'Full-Stack',
achievements: 80,
points: '1000'
},
{
name: 'Nick',
track: 'AI',
achievements: '60',
points: '1000'
}
];
let search = prompt('Type name of student');
while (search !== 'quit') {
const filteredList = students.filter(function(student) {
return student.name === search;
});
let message = '';
if (filteredList.length > 0) {
for (const student of filteredList) {
message += '<h1>' + student.name + '</h1>';
message += '<p>' + student.track + '</p>';
message += '<p>' + student.achievements + '</p>';
message += '<p>' + student.points + '</p>';
}
alert(message);
} else {
alert('That student does not exist. Try again');
}
search = prompt('Type name of student');
}
In order to avoid looping through the entire array each time you want to show a message for the user, making an object from the array is the best approach.
for example:
var students=[
{
id: 1,
name:'Chris',
track:'IOS',
achievements:'100',
points:'1000'
},
{
id: 2,
name:'John',
track:'Web Design',
achievements:'90',
points:'1000'
},
{
id: 3,
name:'Brent',
track:'Front-End',
achievements:'70',
points:'1000'
},
{
id: 4,
name:'Josh',
track:'Full-Stack',
achievements:80,
points:'1000'
},
{
id: 5,
name:'Nick',
track:'AI',
achievements:'60',
points:'1000'
}
];
const arrayToObject = (array) =>
array.reduce((obj, item) => {
obj[item.id] = item
return obj
}, {});
const studentsObject = arrayToObject(students);
console.log(studentsObject);
console.log(studentsObject[2]);

Select category not updating sub category

This is suppose to look at the users category selection and then update the subcategory. The solution was recommended by someone else but I can't seem to get it to work. When I select the category, subcategory doesn't update. Can someone let me know what I'm missing.
Path: category.js
<template name="category">
{{#autoForm collection="Meteor.users" id="categoryForm" doc=currentUser type="update"}}
{{> afQuickField name='profile.categories'}}
{{/autoForm}}
</template>
Path: Schema.js
var fruitArr = ['apple', 'banana'];
var vegetablesArr = ['carrot', 'broccoli'];
Schema.Category = new SimpleSchema({
category: {
type: String,
label: "Category",
allowedValues: ['fruit', 'vegetables']
},
subcategory: {
type: String,
label: "Subcategory",
allowedValues: _.union(fruitArr, vegetablesArr),
autoform: {
options: function () {
let category = AutoForm.getFieldValue("category");
if (!category) return [{label: "Please select a category first", value: ""}];
if (category === "fruit") return _.map(fruitArr, (v, i) => ({
label: "Fruit " + (i + 1) + ": " + v,
value: v
}));
else return _.map(vegetablesArr, (v, i) => ({label: "Vegetables " + (i + 1) + ": " + v, value: v}));
}
}
}
});
Schema.UserProfile = new SimpleSchema({
categories: {
type: Schema.Category,
optional: true,
}
});
When calling AutoForm.getFormValues('categoryForm'); in the browser's console log, the following result will be returned:
{
"insertDoc":{
"profile":{
"categories":{
"category":"fruit"
}
}
},
"updateDoc":{
"$set":{
"profile.categories.category":"fruit"
},
"$unset":{
"profile.categories.subcategory":""
}
}
}
As you can see from above, the schema field subcategory is referenced as profile.categories.subcategory. Therefore, the field subcategory won't be updated because AutoForm.getFieldValue("category"); returns undefined.
You can fix this error by changing
let category = AutoForm.getFieldValue("category");
to
let category = AutoForm.getFieldValue("profile.categories.category");
inside your options function in the subcategory schema field.

Categories

Resources