Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
So i have for example such an object:
let htmlDom = [
{
type: "div",
att: {
class: ["test", "test2"],
id: "yoyo",
"data-hello": ["jan", "john"],
},
content: "Hello",
child: [
{
type: "div",
content: "test",
child: [{}],
},
],
},
];
And now i want to dynamicly acess it, for example: htmlDom[0].child[0].child[0], and now the numbers of children can vary and the numbers generally. lets say for example i have an array of [0, 0, 0], How can i then acess that path dynamicly by using the numbers in the array, and then change the content of the last child[0]? The thing is that for example the array can later on vary to [0,2,4,3,6] and such, and depending on the array lenght i need to create more .child[]
You can first grab the last index from your array of indexes using .pop(), and then use .reduce() on the now modified indexes array to iterate over your children arrays. By setting the accumulator as the starting htmlDom you can access the objects at each index and its child array for each iteration of .reduce(), where the child array from each object is returned. This child array is then used as the acc for the next call/iteration of your reduce method. Once you have found the last child array, you can use index which you previously popped off your array to set/update the value:
let htmlDom = [ { type: "div", att: { class: ["test", "test2"], id: "yoyo", "data-hello": ["jan", "john"], }, content: "Hello", child: [ { type: "div", content: "test", child: [{}], }, ], }, ];
const changeByIdxs = (arr, [...idxs], value) => {
const lastIdx = idxs.pop();
const finalArr = idxs.reduce((acc, i) => acc[i].child, arr);
finalArr[lastIdx] = value;
}
changeByIdxs(htmlDom, [0, 0, 0], {x: 1});
console.log(htmlDom);
The above can be implemented with for loops if you find that easier to understand:
const htmlDom = [{ type: "div", att: { class: ["test", "test2"], id: "yoyo", "data-hello": ["jan", "john"], }, content: "Hello", child: [{ type: "div", content: "test", child: [{}], }, ], }, ];
const changeByIdxs = (arr, [...idxs], value) => {
const lastIdx = idxs.pop();
let finalArr = arr;
for (let i = 0; i < idxs.length; i++) {
const idx = idxs[i];
finalArr = finalArr[idx].child;
}
finalArr[lastIdx] = value
}
changeByIdxs(htmlDom, [0, 0, 0], {x: 1});
console.log(htmlDom);
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 24 days ago.
Improve this question
So basically I have an array of ids and I want to return only a simple array which I want to look like this
*[1,2,3]*
instead of
*[0:[1] , 1:[2]]*
Is there any way to do it
Code
const usersWhoHavePurchasedYourCourses = usersWhoHaveTokens.filter(
(user1: any) => {
return user1.tokens
?.map((token: any) => parseInt(token.course_id))
.includes(user.courses?.map((course: any) => course.id));
});
The output looks like this
output
As I said I don`t want to return this kind of output.
Edit
In attempting to reverse-engineer your logic, wouldn't you want to filter by checking if a user has at least one course? I recommend using Array.prototype.some as your filter result.
const user = { courses: [{ id: 1 }, { id: 2 }] };
const usersWhoHaveTokens = [
{ id: 1, tokens: [{ course_id: '1' }] },
{ id: 2, tokens: [{ course_id: '2' }] },
{ id: 3, tokens: [{ course_id: '3' }] },
];
// Compute the set, for faster processing
const userCourseIds = new Set(user.courses.map((course) => course.id));
const usersWhoHavePurchasedYourCourses = usersWhoHaveTokens
.filter(({ tokens }) => tokens
.some((token) => userCourseIds.has(parseInt(token.course_id))))
.map(({ id }) => id);
console.log(usersWhoHavePurchasedYourCourses); // [1, 2]
Original response
If you object is an 'object' type, you will need to transform it into an array, and then flatten it.
const
obj = { 0: [1], 1: [2] },
arr = Object.values(obj).flat();
console.log(JSON.stringify(arr)); // [1, 2]
If you want to preserve indices:
const
obj = { 1: [2], 5: [6] },
arr = Object.entries(obj).reduce((acc, [index, value]) => {
acc[+index] = value;
return acc;
}, []).map(([value]) => value);
console.log(JSON.stringify(arr)); // [null, 2, null, null, null, 6]
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
i have this array
const names = [
{ name: 'Anna' },
{ num: 27 },
{ name: 'Valeria', age: 20},
{ secondname: 'Wilson' },
{ age: 12, name: 'Max' },
{ weight:'50kg', height: '172cm', name: 'Nick' }
]
using reduce i need to create new array that contains all names from initial array
i made like this, but i think it is bad
let allNames = names.reduce((previousValue, names) =>{
return previousValue + names.name},[])
console.log(allNames)
i did
allName.push(ar.name);
return allName;
}, []);
console.log(names);```
Array.reduce()
You have multiple options. You can use Array.reduce to merge them all into one array. You just need to check if name is defined.
names.reduce((allNames, person) => {
if (person.name) {
return [...allNames, person.name];
}
return allNames;
}, []);
Array.forEach()
Same for Array.forEach:
const allNames = [];
names.forEach((person) => {
if (person.name) {
allNames.push(person.name);
}
});
allNames;
Instead, I would recommend using Array.filter to remove all people without a name and map (Array.map) over them, to just return the names.
In terms of runtime, this would require you to loop twice over the array, but I think this is way more readable
Array.filter / Array.map
names
.filter((person) => person.name)
.map((person) => person.name);
Using reduce by including a check for objects that don't have the name property and an empty array as initial value:
const names = [
{ name: 'Anna' },
{ num: 27 },
{ name: 'Valeria', age: 20},
{ secondname: 'Wilson' },
{ age: 12, name: 'Max' },
{ weight:'50kg', height: '172cm', name: 'Nick' }
]
const reduceResult = names.reduce((previous, current) => {
if(current.name)
{
previous.push(current.name);
}
return previous;
}
,
[]);
console.log(reduceResult);
/*
[
"Anna",
"Valeria",
"Max",
"Nick"
]
*/
Using map, you will have undefined for objects that don't have a name property:
const mapResult = names.map(x => x.name);
console.log(mapResult);
/*
[
"Anna",
undefined,
"Valeria",
undefined,
"Max",
"Nick"
]
*/
filter + map can also be used but performance talking reduce is a better choice.
const filterMapResult = names.filter(({name}) => !!name).map(x => x.name);
console.log(filterMapResult);
/*
[
"Anna",
"Valeria",
"Max",
"Nick"
]
*/
This question already has answers here:
Flatten Nested Objects and Output Array
(2 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I have the following, nested, json structure:
{
element: "a",
items: ["l1", "l2"],
children: [{
element: "b",
children: [{
element: "c",
items: ["l3"]
}, {
element: "b",
child: {
element: "c",
items: ["l4"]
}
}
Basically:
An "element" contains a name, a list of items, and a list of children being an "element" themselves
The items list can be missing
I'd like to process this json to get a flat array containing all the items:
const finalArray = parse(json); //returns ["l1","l2","l3","l4"]
Is there any elegant way to achieve this with mapping/filter functions?
const getItems = (acc = [], { items = [], child = {}, children = [] }) =>
[
...acc,
...items,
...(child?.items || []),
...(children.length ? children.reduce(getItems, acc) : [])
];
const data = {
element:"a",
items: ["l1","l2"],
children: [
{
element: "b",
children: [
{ element: "c", items: ["l3"] },
{ element: "b", child: { element: "c", items: ["l4"] } }
]
}
]
}
console.log( getItems([], data) );
This question already has answers here:
How do I loop through or enumerate a JavaScript object?
(48 answers)
Closed 3 years ago.
I have my object that is stored in variable val.Main.widgets. I also have variable that functions as a data tree. What I need is to generate as many children elements as there are keys in my object that is stored in val.Main.widgets. If I console log this: console.log(Object.keys(val.Main.widgets).length;, it returns 8 so in this case I need to generate 8 children elements.
I am guessing I need some sort of cycle, but I really dont know where to start, hence why I am asking here.
Here is my object:
And here is my tree variable:
const tileTree = [
{
name: val.Main.name,
children: [
{
name: val.Main.widgets['E1EV7'].name,
},
{
name: val.Main.widgets['E70ZT'].name,
},
],
},
];
Thank you for any advice.
You do not need lodash for this. You want to use Array.map on the result of Object.keys.
const content = val.Main.widgets;
const keys = Object.keys(content);
const children = keys.map(key => content[key]);
Then in your tileTree you simply set children to children.
const tileTree = [
{
name: val.Main.name,
children,
},
];
This will give you all the properties of the val.Main.widgets object. If you only want specific ones, you can destructure them in your map function.
...
// Suppose we only want 'name'.
const children = keys.map(key => {
const { name } = content[key];
return { name };
});
...
You can use
Object.keys(val.Main.widgets).map(widgetKey => {
const widget = val.Main.widgets[widgetKey]
return (
<div key={widgetKey}>
<h1>This is widget : {widget.name}</h1>
</div>
)
}
const tileTree = [
{
name: 'test',
children: [
{
name: 'test1'
},
{
name: 'test2'
},
{
name: 'test3'
},
{
name: 'test4'
},
{
name: 'test5'
},
{
name: 'test6'
}
],
},
];
const createLi = (arr) => {
for (let i = 0; i < arr[0].children.length; i += 1) {
let li = document.createElement("li");
let ol = document.getElementById('list');
li.innerHTML = arr[0].children[i].name;
ol.appendChild(li);
}
}
createLi(tileTree);
https://jsfiddle.net/os20wdLh/
Object.keys is a function that returns an array. The array has a foreach method
var lunch = {
sandwich: 'ham',
snack: 'chips',
drink: 'soda',
desert: 'cookie',
guests: 3,
alcohol: false,
};
Object.keys(lunch).forEach(function (item) {
console.log(item); // key
console.log(lunch[item]); // value
});
Other way is by using lodash module from npm or yarn. example where _ is the lodash module:
_.times(8, i => {
schedule.push({
date: moment()
.add(i, "days")
.format("YYYY-MM-DD"),
times: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
});
});
You can replace the 8 with your .length code.
This question already has answers here:
Merge property from an array of objects into another based on property value lodash
(5 answers)
Closed 4 years ago.
I have 2 array of objects
The first one called data:
const data = [
{
id: 1,
nombre: 'Piero',
},
{
id: 4,
nombre: 'Nelson',
},
{
id: 7,
nombre: 'Diego'
},
]
and the second called subs:
const subs = [
{
id: 1,
name: 'Temprano',
},
{
id: 4,
name: 'A tiempo',
},
{
id: 7,
name: 'Tarde'
},
]
In which I want to compare that if they have the same ID, the subs array will pass its name value to it and if it does not match that it puts a '-' in the data array, try this way:
data.forEach((d)=>{
subs.forEach((s)=>{
if(d.id === s.id){
d.subname = s.name;
}
else {
d.subname = '-';
}
});
});
But always assign the values with '-' as if it does not match any. What part am I doing wrong? Is there any other simpler way to do this? I would greatly appreciate your help.
The size of the subs array may vary.
It looks like you are not exiting the inner loop when a successful match is found.
In the first example where you are looking for a match for Piero, in your first iteration 1===1 and d.subname is correctly set to 'Temprano'. However, you then continue to compare the values- 1 !== 4 so Temprano is overwritten with '-', and 1 !== 7 so it is overwritten again.
An alternate approach:
data.forEach(d => {
const match = subs.find(s => s.id === d.id);
d.subname = match ? match.name : '-';});
I'd also recommend adding a case where you're not expecting to find a match, so you can see that it works in both cases!
https://codepen.io/anon/pen/MGGBLP?editors=0010
const data = [
{
id: 1,
nombre: 'Piero',
},
{
id: 4,
nombre: 'Nelson',
},
{
id: 7,
nombre: 'Diego'
},
];
const subs = [
{
id: 1,
name: 'Temprano',
},
{
id: 4,
name: 'A tiempo',
},
{
id: 7,
name: 'Tarde'
},
];
// by caching one of the arrays in an object, it reduces the run time to linear.
const obj = subs.reduce((acc, item) => {
acc[item.id] = item;
return acc;
})
data.forEach(d => {
if (d.id in obj) {
d.subname = obj[d.id].name;
} else {
d.subname = '-';
}
});
console.log(data);
You just need two lines for this:
var findIds = id => subs.find(findId => findId.id === id);
data.forEach(findId => Object.assign(findId, findIds(findId.id)));
Your data array object should now include the name property from it's respective id sharing object in subs array.
jsFiddle: https://jsfiddle.net/AndrewL64/9k1d3oj2/1/