Why JavaScript deconstructing not working inline - javascript

In hash color function, I assign title and rating by deconstructing them from another object but instead it assign the full color object, and if I break the code into two lines (first deconstructing and assign(reconstructing) then it works fine. Please explain the logic running behind this.
const colors = [{
id: '-xekare',
title: "rad red",
rating: 3
}, {
id: '-jbwsof',
title: "big blue",
rating: 2
}, {
id: '-prigbj',
title: "grizzly grey",
rating: 5
}, {
id: '-ryhbhsl',
title: "banana",
rating: 1
}]
hashcolor=colors.reduce((hash,color)=>{
hash[color.id]={title,rating}=color
return hash
},[])
console.log(hashcolor);

When you're destructuring, you're creating (or assigning to) individual variables - you're not creating a new object, so hash[color.id] = { title, rating } doesn't actually work. You could write (or copy) a pick function that accomplishes that, but in this case, you could just destructure in the arguments instead.
You should also probably use an object as the accumulator, rather than an array:
const colors = [{
id: '-xekare',
title: "rad red",
rating: 3
}, {
id: '-jbwsof',
title: "big blue",
rating: 2
}, {
id: '-prigbj',
title: "grizzly grey",
rating: 5
}, {
id: '-ryhbhsl',
title: "banana",
rating: 1
}]
const hashcolor = colors.reduce((hash, { id, title, rating }) => {
hash[id] = { title, rating };
return hash;
}, {})
console.log(hashcolor);

Although it looks the same, there are actually 2 phases to a creation of an object from destructured properties:
Destructure and assign the properties to variables (or consts) - const { title, rating } = color.
Use shorthand property names to create the object const obj = { title, rating }.
In your case you can move the destructuring phase to the function call:
const colors = [{"id":"-xekare","title":"rad red","rating":3},{"id":"-jbwsof","title":"big blue","rating":2},{"id":"-prigbj","title":"grizzly grey","rating":5},{"id":"-ryhbhsl","title":"banana","rating":1}]
const hashcolor = colors.reduce((hash, { id, title, rating }) => {
hash[id] = { title, rating }
return hash
}, {})
console.log(hashcolor);
And you can also use object rest, to create the object:
const colors = [{"id":"-xekare","title":"rad red","rating":3},{"id":"-jbwsof","title":"big blue","rating":2},{"id":"-prigbj","title":"grizzly grey","rating":5},{"id":"-ryhbhsl","title":"banana","rating":1}]
const hashcolor = colors.reduce((hash, { id, ...newColor }) => {
hash[id] = newColor
return hash
}, {})
console.log(hashcolor);

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

How to fix an error when working with an array and the find method?

I have a question about working with the find method. I have a task - I need to go through the array and find a match with a specific string. But at the same time there is a condition that this string can be inside one of the objects already in its child array. I make an if construct in my function to check this when passing through the array, but it does not work out as I expected. Tell me, please, where I went wrong.
P.S. I write more correctly. If the array object "newList" has "items" , then you need to look for comparison not in the object, but in its "items" array among "role" . If "items" does not exist for the object, then we look for a match in this object among "role"
const newList = [
{
role: "role111",
title: "title1",
},
{
role: "role222",
title: "title2",
},
{
role: "role333",
title: "title3",
},
{
role: "role444",
title: "title4",
items: [{
role: "role555",
title: "title5",
}, {
role: "role666",
title: "title6",
}, {
role: "role777",
title: "title7",
},]
},
{
role: "role888",
title: "title8",
},
];
const url = "role7";
export const findAfterRefresh = (list, url) =>
list.find((item) => {
if (item.items && item.items?.length > 0) {
return item.items.find((childrenITem) => childrenITem.role.includes(url));
}
return item.role.includes(url);
});
;
findAfterRefresh(newList, url);
Your solution was close, but if you call find on newList, it can only ever return one of the elements of newList, it can't return an element from the items array of one of those elements. That plus the fact you want the role value, not the element itself, makes the find method not a good match for your current data structure (but keep reading; if you really want to use find, there's a way).
Instead, a simple loop with recursion does the job:
/*export*/ const findAfterRefresh = (list, url) => {
for (const item of list) {
if (item.role?.includes(url)) {
return item.role;
}
if (item.items?.length) {
const childRole = findAfterRefresh(item.items, url);
if (childRole) {
return childRole;
}
}
}
};
Here's a version with explanatory comments:
/*export*/ const findAfterRefresh = (list, url) => {
// Loop the given list...
for (const item of list) {
// ...check this item's role
// (Remove v-- this `?` if `role` will always be there)
if (item.role?.includes(url)) {
return item.role;
}
// If this item has child items, check them
if (item.items?.length) {
// Search child items using recursion
const childRole = findAfterRefresh(item.items, url);
if (childRole) {
// Found it, return it
return childRole;
}
// Didn't find it, keep looping
}
}
};
Live Example:
const newList = [
{
role: "role1",
title: "title1",
},
{
role: "role2",
title: "title2",
},
{
role: "role3",
title: "title3",
},
{
role: "role4",
title: "title4",
items: [
{
role: "role5",
title: "title5",
},
{
role: "role6",
title: "title6",
},
{
role: "role7plusotherstuff",
title: "title7",
},
],
},
];
/*export*/ const findAfterRefresh = (list, url) => {
// Loop the given list...
for (const item of list) {
// ...check this item's role
// (Remove v-- this `?` if `role` will always be there)
if (item.role?.includes(url)) {
return item.role;
}
// If this item has child items, check them
if (item.items?.length) {
// Search child items using recursion
const childRole = findAfterRefresh(item.items, url);
if (childRole) {
// Found it, return it
return childRole;
}
// Didn't find it, keep looping
}
}
};
console.log("Searching for 'role7'");
console.log(findAfterRefresh(newList, "role7"));
console.log("Searching for 'role2'");
console.log(findAfterRefresh(newList, "role2"));
Note: I added a bit to the role containing role7 so you could see that the code returns the full role, not just the bit in url.
But if you really want to use find, you can do it by first creating a flat array of roles:
// Creates a new array of `role` values (the array may also contain
// `undefined`, if the `role` property of any element or child element is
// `undefined`)
const flattenRoles = (list) =>
(list ?? []).flatMap((item) => [item.role, ...flattenRoles(item.items)]);
/*export*/ const findAfterRefresh = (list, url) => {
return flattenRoles(list).find((role) => role?.includes(url));
};
That code's a big shorter, but note that it creates a number of temporary arrays, and it always works its way through the full list before looking for roles, whereas the earlier version stops looking as soon as it's found a matching role. That's unlikely to be a problem if newList is of a reasonable size, but it's worth keeping in mind. (I'd probably use the earlier version, not this.)
Here's that in action:
const newList = [
{
role: "role1",
title: "title1",
},
{
role: "role2",
title: "title2",
},
{
role: "role3",
title: "title3",
},
{
role: "role4",
title: "title4",
items: [
{
role: "role5",
title: "title5",
},
{
role: "role6",
title: "title6",
},
{
role: "role7plusotherstuff",
title: "title7",
},
],
},
];
// Creates a new array of `role` values (the array may also contain
// `undefined`, if the `role` property of any element or child element is
// `undefined`)
const flattenRoles = (list) =>
(list ?? []).flatMap((item) => [item.role, ...flattenRoles(item.items)]);
/*export*/ const findAfterRefresh = (list, url) => {
return flattenRoles(list).find((role) => role?.includes(url));
};
console.log("Searching for 'role7'");
console.log(findAfterRefresh(newList, "role7"));
console.log("Searching for 'role2'");
console.log(findAfterRefresh(newList, "role2"));
In a comment you've asked:
ESLint: iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.(no-restricted-syntax)
...how much do you think eslnit is right in this case?
That's up to you. If your target environment is ES2015+, there's no need for regenerator-runtime. As far as I know, there are no major pre-ES2015+ environments (IE11 is obsolete and discontinued). But if you need to support it and want to avoid regenerator-runtime, you can replace the for-of loop with some and assigning to a closed-over variable:
const newList = [
{
role: "role1",
title: "title1",
},
{
role: "role2",
title: "title2",
},
{
role: "role3",
title: "title3",
},
{
role: "role4",
title: "title4",
items: [
{
role: "role5",
title: "title5",
},
{
role: "role6",
title: "title6",
},
{
role: "role7plusotherstuff",
title: "title7",
},
],
},
];
/*export*/ const findAfterRefresh = (list, url) => {
// Loop the given list...
let role;
list.some((item) => {
// ...check this item's role
// (Remove v-- this `?` if `role` will always be there)
if (item.role?.includes(url)) {
role = item.role;
return true;
}
// If this item has child items, check them
if (item.items?.length) {
// Search child items using recursion
const childRole = findAfterRefresh(item.items, url);
if (childRole) {
// Found it, return it
role = childRole;
return true;
}
// Didn't find it, keep looping
}
});
return role;
};
console.log("Searching for 'role7'");
console.log(findAfterRefresh(newList, "role7"));
console.log("Searching for 'role2'");
console.log(findAfterRefresh(newList, "role2"));
The return true; statements tell some that you're done, it can stop looping.
...and the error on childRole from typescript is TS7022: 'childRole' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
The TypeScript error is just because the code doesn't have type annotations, because it was a JavaScript question, not a TypeScript question. :-) If you add the appropriate type annotations, it'll be fine.
As per my understanding, You are trying to filtered out the newList with all the objects includes role7 string in role property either in main object or in the child array objects. If Yes, You have to use Array.filter() method instead of Array.find() as it will only returns the first element in the provided array that satisfies the provided testing function.
Live Demo :
const newList = [
{
role: "role111",
title: "title1",
},
{
role: "role777",
title: "title2",
},
{
role: "role333",
title: "title3",
},
{
role: "role444",
title: "title4",
items: [{
role: "role555",
title: "title5",
}, {
role: "role666",
title: "title6",
}, {
role: "role777",
title: "title7",
},]
},
{
role: "role888",
title: "title8",
},
];
const url = "role7";
const findAfterRefresh = (list, url) =>
list.filter((item) => {
return (item.role.includes(url)) ? item :
item.items = item.items?.filter((childrenITem) => childrenITem.role.includes(url));
});
console.log(findAfterRefresh(newList, url));
After reading some of the other solutions I was able to refactor my code down a little bit. I decided to convert the raw array value into a 2d array, each nested array value would hold the role and item values.
I chose to do things this way so that the output of this function can still be used for any purpose regarding the roles and titles of these items. and to create an array which would be easy to iterate through.
const url = "role6";
const findAfterRefresh = (list) =>{
let foundData = []
list.find((item) => {
foundData.push([item.role, item.title]);
if (item.items !== undefined) {
item.items.find((item) => {
foundData.push([item.role, item.title]);
})
}
});
return foundData
}
for(let v in findAfterRefresh(newList) ){
if (findAfterRefresh(newList)[v].includes(url)) {
console.log(findAfterRefresh(newList)[v].includes(url)); break
}
}
//output of findAfterRefresh: [[role1,title1],[role2,title2][... etc
//ouput of the for loop at the bottom: true...
Ok I got to it.
Edit: I didn't read the title carefully, the code at the bottom is with for instead of "find" I hope it's not a problem
const newList = [
{
role: "role1",
title: "title1",
},
{
role: "role2",
title: "title2",
},
{
role: "role3",
title: "title3",
},
{
role: "role4",
title: "title4",
items: [{
role: "role5",
title: "title5",
}, {
role: "role6",
title: "title6",
}, {
role: "role7",
title: "title7",
},]
}
];
const url = "role2";
const findAfterRefresh = (list, url) => {
for(const singleItem of Object.values(list)) {
if(singleItem.items && singleItem.items?.length > 0) {
return singleItem.items.find(child => child.role === url);
};
if(!singleItem.role.includes(url)) {
continue;
};
return singleItem;
}
};
findAfterRefresh(newList, url);

How do I populate an array of objects where every object has an array inside of it using response from rest API?

I can't google the right solution for this for about an hour straight,
So I'm getting a response from the API that looks like this:
[
{
"Name": "name1",
"Title": "Name One",
"Children": [
{
"Name": "Name 1.1",
"Title": "Name one point one"
},
]
And I need it to fit this kind of "mold" for the data to fit in:
{
title: 'Name One',
value: 'name1',
key: '1',
children: [
{
title: 'Name one point one',
value: 'Name 1.1',
key: 'key1',
},
I am trying to achieve this using a foreach but It's not working as intended because I need to do this all in one instance of a foreach.
Here's what I gave a go to(vue2):
created() {
getData().then(response => {
const formattedResponse = []
response.forEach((el, key) => {
formattedResponse.title = response.Title
formattedResponse.name = response.Name
formattedResponse.children = response.Children
})
})
Use map over the main array and use destructuring assignment to extract the properties by key, and relabel them, and then do exactly the same with the children array. Then return the updated array of objects.
const data=[{Name:"name1",Title:"Name One",Children:[{Name:"Name 1.1",Title:"Name one point one"}]},{Name:"name2",Title:"Name Two",Children:[{Name:"Name 1.2",Title:"Name one point two"}]}];
const result = data.map((obj, key) => {
const { Title: title, Name: value } = obj;
const children = obj.Children.map(obj => {
const { Title: title, Name: value } = obj;
return { title, value, key: (key + 1).toString() };
});
return { title, value, children };
});
console.log(result);
Your API response is JSON. All you need to do is:
var resp=JSON.parse(API response);

How to find the id of an object within an array having only one identified attribute of the "child" array

I would like help to develop a function using the most optimized approach in Javascript to find the id of the "parent" object having only a code of an "child" object (inside dataArray).
Example:
getIdParent("240#code") -> return "1"
[
{
id: 0,
dataArray:[
{
id: 182,
code: "182#code",
name: "Product1"
},
{
id: 183,
code: "183#code",
name: "Product2"
}
]
},
{
id: 1,
dataArray:[
{
id: 240,
code: "240#code",
name: "Product2"
},
{
id: 341,
code: "341#code",
name: "Product2"
}
]
}
]
Thanks in advance.
You don't really have a lot of options here.
The only real optimisation I can think of is based on how often you expect to call this function.
If it's only once, you just need to iterate the array, searching for values and return as early as possible to prevent unnecessary iterations.
function getIdParent(childCode) {
return arr.find(parent =>
parent.dataArray.some(({ code }) => code === childCode))?.id
}
If multiple times, you should build up an indexed map of child code to parent object and then reference that
const arr = [{"id":0,"dataArray":[{"id":182,"code":"182#code","name":"Product1"},{"id":183,"code":"183#code","name":"Product2"}]},{"id":1,"dataArray":[{"id":240,"code":"240#code","name":"Product2"},{"id":341,"code":"341#code","name":"Product2"}]}]
const codeMap = arr.reduceRight((map, parent) => {
parent.dataArray.forEach(({ code }) => {
map.set(code, parent)
})
return map
}, new Map())
function getIdParent(code) {
return codeMap.get(code)?.id
}
let search = ["240#code", "182#code", "NotFound"]
search.forEach(code => {
console.log("Parent ID for", code, "=", getIdParent(code))
})

Loop through and delete elements in an array of objects

In my Vue.js project I have an array of objects which I want to list through and display in the browser.
My array contains four objects, I want to display only 3. The way I choose the 3 objects are dependent on a preference setting that the user has chosen somewhere else in the project and stored in a variable (below it is called userPreference). I am currently stuck on the best and most efficient way to remove one of the objects from my array based on the userPreference value.
My v-for in my template
<ul v-for="item in getOutroItems"><li>item<li></ul>
My object:
data() {
return {
outroItems: [{ title: "outro1", text: "XYZ" }, { title: "outro2", text: "ABC" }, { title: "outro3",
text`enter code here`: "QRS" }, { title: "outro4", text: "TUV" }],
userPreference: ""
};
}
My computed property (this is what I have so far)
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
/// here I want to remove outro2 from my array and return an array with the other 3 values
} else (this.userPreference === "noNewsletter") {
/// here I want to remove outro3 from my array and return an array with the other 3 values
}
})
}
So, what is the best way to remove a specific element from an array?
Thanks in advance, and let me know if anything wasn't clear enough.
Your requirement can be fulfilled by below code as array.filter just wants true or false in its return to accept or remove an element from its array.
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
// here I want to remove outro2 from my array and return an array with the other 3 values
return value.title != 'outro2';
} else (this.userPreference === "noNewsletter") {
// here I want to remove outro3 from my array and return an array with the other 3 values
return value.title != 'outro3';
}
})
}
However if you want to not create another array if it is big. you should go with swapping such elements to be removed with the end indexed element in the array and popping those many elements from the array.
There are multiple ways of getting the correct items from an array.
My preferred method and in your example: Using array.filter
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const leftOverItems = outroItems.filter((item) => item.title !== "outro2");
console.log(leftOverItems);
Another option is to find the index of the item to remove and then remove it with splice
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const itemToDelete = outroItems.find((item) => item.title === "outro2");
const indexToDelete = outroItems.indexOf(itemToDelete);
outroItems.splice(indexToDelete, 1);
console.log(outroItems);
Combining any of the functions above with a function will prevent you from writing duplicate code.
const itemToRemove = (arr, attr, name) => {
return arr.filter((item) => item[attr] !== name);
}
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
// Remove from "outroItems" where "title" is "outro2"
const removed2 = itemToRemove(outroItems, "title", "outro2");
// Remove from "outroItems" where "title" is "outro3"
const removed3 = itemToRemove(outroItems, "title", "outro3");
// Remove from "outroItems" where "text" is "TUV"
const removedTUV = itemToRemove(outroItems, "text", "TUV");
console.log(removed2);
console.log(removed3);
console.log(removedTUV);

Categories

Resources