Extract nodeList item ids as an array with array map - javascript

I want to save a set of ids based on a nodeList returned by a querySelector.
I achieved it with a very procedural approach, thinking in terms of steps.
Select items
Create an array
Iterate over items
Get the id
Push it into the array
const getThem = document.querySelectorAll('.get-me');
let arrayOfData = [];
Array.prototype.forEach.call (getThem, function (node){
arrayOfData.push(node.id);
});
console.log(arrayOfData);
<ul>
<li class="get-me" id="one">One</li>
<li class="get-me" id="two">Two</li>
<li class="get-me" id="three">Three</li>
</ul>
I wonder if I can get this done with array map, to reduce the amount of lines and also take advantage of modern Javascript features.
I tried with:
const getThem = document.querySelectorAll('.get-me');
Array.prototype.map.call(getThem, (item) => ({ [id]: item.id }));
console.log(getThem);
But it doesn't work.

Unfortunately, the NodeList that is returned by .querySelectorAll() doesn't have a .map() method. However, as it is iterable, you can pass it to Array.from(), and use the second mapping argument to perform your mapping:
const getThem = document.querySelectorAll('.get-me');
const arrayOfData = Array.from(getThem, node => node.id);
console.log(arrayOfData);
<ul>
<li class="get-me" id="one">One</li>
<li class="get-me" id="two">Two</li>
<li class="get-me" id="three">Three</li>
</ul>
This does two things in one go, it converts your NodeList to an array, and it performs the mapping based on the second argument. This is opposed to doing something like Array.from(getThem).map(...) and [...getThem].map(...) which both do two iterations over your items.
I tried with:
const getThem =
document.querySelectorAll('.get-me');
Array.prototype.map.call(getThem, (item) => ({ [id]: item.id }));
console.log(getThem);
But it doesn't work.
This does also work, you just need to store the return value in a variable a log that. .map() doesn't change the original array, it returns a new one instead:
const getThem = document.querySelectorAll('.get-me');
const res = Array.prototype.map.call(getThem, (item) => item.id);
console.log(res);
<ul>
<li class="get-me" id="one">One</li>
<li class="get-me" id="two">Two</li>
<li class="get-me" id="three">Three</li>
</ul>

Another way to do it using spread operator and array map like below
const getThem = document.querySelectorAll('.get-me');
const nodes = [...getThem];
const arrayOfData = nodes.map(node => node.id);
console.log(arrayOfData);
<ul>
<li class="get-me" id="one">One</li>
<li class="get-me" id="two">Two</li>
<li class="get-me" id="three">Three</li>
</ul>

You can do:
const elems = document.querySelectorAll('.get-me')
const arrayOfData = Array.from(elems).map(({ id }) => id)
console.log(arrayOfData)
<ul>
<li class="get-me" id="one">One</li>
<li class="get-me" id="two">Two</li>
<li class="get-me" id="three">Three</li>
</ul>

Related

Looping <li> elements in JSX React

In React.js documentation, I wonder how Array.map() is used in JSX React.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
in <ul> tag, why we just put the variable listItems directly?. Because I think it will return a single array instead of <li> elements like this :
<ul>
[
<li></li>,
<li></li>,
<li></li>,
]
</ul>
how does JSX treat an array?
Did I have to loop listItems manually?
Thank you in advance.
you might want to take a look here: https://stackabuse.com/how-to-loop-in-react-jsx/ . I hope this is what you are looking for
The map() method is the most commonly used function to iterate over an array of data in JSX. You can attach the map() method to the array and pass a callback function that gets called for each iteration. When rendering the User component, pass a unique value to the key prop.
JSX treat an array like a multiple elements. You can code like this:
function NumberList() {
return (
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
);
}
Or use an array. But when use an array, we need to add an unique key attribute for each element in array:
function NumberList() {
const listItems = [
<li key="1">1</li>,
<li key="2">2</li>,
<li key="3">3</li>,
];
return (
<ul>{listItems}</ul>
);
}
If you want to using one element, you can wrap all elements to a Fragment:
function NumberList() {
const listItems = (
<>
<li>1</li>
<li>2</li>
<li>3</li>
</>
);
return (
<ul>{listItems}</ul>
);
}
Use map is same as using array:
function NumberList() {
const numbers = [1, 2, 3];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
or using map directly in JSX:
function NumberList() {
const numbers = [1, 2, 3];
return (
<ul>
{
numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
)}
</ul>
);
}
About key attribute, please refer to React's document: (https://reactjs.org/docs/lists-and-keys.html#keys)[https://reactjs.org/docs/lists-and-keys.html#keys].

Function to create multidimensional object

Hello I'm strugglling with write everything from multi depth ul list into one object. I've got something like this.
Depth One 1Depth Two 1Depth Three 1Depth Three 1Depth Two 1
Depth One 2Depth Two 2Depth Three 2Depth Two 2Depth Three 2Depth Three 2Depth Three 2
Depth One 3Depth Two 3Depth Three 3Depth Three 3Depth Three 3Depth Three 3Depth Three 3Depth Three 3Depth Three 3Depth Two 3
And i want to create one object with values from it. I want to make it like
const object = {
0:{
item : Depth One 1,
length: length of Depth Two,
values:{
0:{
item: Depth One 1 => Depth Two 1(1st item)
length: length: length of Depth Three,
values:{
0:{
item: Depth One 1 => Depth Two 1 => Depth Three 1(1st item)
}
1:{
item: Depth One 1 => Depth Two 1 => Depth Three 1(2nd item)
}
}
}
}
}
}
I've tried to do it with 3 for loops, but I didn't get the result that I wanted.
Thanks for all answers!
My code:
export function object(li, li2, li3, heightOfLi, widthOfLi) {
let obj = {};
for (let i = 0; i < li.length; i++) {
let obj2 = {};
if (li2[i] == undefined) {
continue;
}
let counter = li2[i].querySelectorAll(".li-depth-2");
if (counter == null) {
continue;
}
for (let j = 0; j < counter.length; j++) {
let obj3 = {};
let counter2 = li3[j].querySelectorAll(".li-depth-3");
if (counter2 == null) {
continue;
}
for (let k = 0; k < counter2.length; k++) {
obj3[k] = {
name: li3[j].querySelectorAll(".li-depth-3 span")[k].textContent,
item: li3[j].querySelectorAll(".li-depth-3")[k],
};
obj2[j] = {
name: li2[i].querySelectorAll(".li-depth-2 span")[j].textContent,
item: li2[i].querySelectorAll(".li-depth-2")[j],
values: obj3,
};
}
}
obj[i] = {
name: li[i].querySelector("span").innerText,
item: li[i],
length: counter.length,
values: obj2,
};
}
return obj;
}
I'm not sure about your markup, but I assumed a markup based on your example as below.
Few points before getting to solution:
To solve the problem, I would rather use reduce than for loop. Which is more convenient.
The algorithm gets the needed elements using querySelector and querySelectorAll and generates the desired object by reducing the array of elements into an object
Writing a generic function to generate an object for level and calling it recursively is neater and easier to scale.
It's more convenient in JavaScript to generate arrays of arrays rather than object of objects. And that's what I did. But you can convert the array into an object easily by using {...array} spread operator.
Finally the desired object is not clear enough but I think you would get the general idea with the result array down below in the snippet.
function getChildren(container, ...levels) {
const parent = container.querySelector(levels[0])
const elements = container.querySelectorAll(levels[0])
return [...elements].reduce((acc, cur) => {
const children = cur.querySelectorAll(levels[1])
return [...acc, {
item: cur.querySelector("span").innerText,
length: [...children].length,
values: getChildren(parent, levels[1], levels[2], null)
}]
}, [])
}
console.log(getChildren(document, ".depth-one", ".depth-two", ".depth-three"))
<ul>
<li class="depth-one">
<span>Depth One 1</span>
<ul>
<li class="depth-two"><span>Depth Two 1<span>
<ul>
<li class="depth-three"><span>Depth Three 1<span></li>
<li class="depth-three"><span>Depth Three 1</span></li>
</ul>
</li>
<li class="depth-two"><span>Depth Two 1</span></li>
</ul>
</li>
<li class="depth-one"><span>Depth One 2</span>
<ul>
<li class="depth-two"><span>Depth Two 2</span>
<ul>
<li class="depth-three"><span>Depth Three 2</span></li>
</ul>
</li>
<li class="depth-two"><span>Depth Two 2</span>
<ul>
<li class="depth-three"><span>Depth Three 2</span></li>
<li class="depth-three"><span>Depth Three 2</span></li>
<li class="depth-three"><span>Depth Three 2</span></li>
</ul>
</li>
</ul>
</li>
<li class="depth-one"><span>Depth One 3</span>
<ul>
<li class="depth-two"><span>Depth Two 3</span>
<ul>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
<li class="depth-three"><span>Depth Three 3</span></li>
</ul>
</li>
<li class="depth-two"><span>Depth Two 3</span></li>
</ul>
</li>
</ul>

How to map over arrays of arrays in JSX

I have a list of names as an array. These names are then paired, into an array in my App.js
I'm looking to map over these to create a <ul> containing <li>s and I'm stuck, any help would be amazing, thanks!
Unfortunately regular map over an array like this won't work... Thanks so!
return (
<ul>
{pairs.map(pair => (
<li key={pair}>{pair}</li>
))}
</ul>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
createPairs() {
const list = [...this.state.list];
list.sort(() => 0.5 - Math.random());
const pairs = [];
while (list.length >= 2) {
const pair = [list.pop(), list.pop()];
pairs.push(pair);
}
this.setState({
pairs
});
}
You can potentially use destructuring within your map arguments to extract the components of each pair. Remember to wrap the destructured arguments in parentheses.
return (
<ul>
{pairs.map(([pair_1, pair_2]) => (
<li key={pair_1}>{pair_1}</li>
))}
</ul>
);

how to use .map() in nodelist in javascript?

Why am i getting this error? How can i access and print the nodes when i'm selecting the <li> tags with querySelectorAll?
script.js:14 Uncaught TypeError: list.map is not a function
HTML
<ul class="wrapper1" id="testDiv">
<li class="cake">Carrots</li>
<li class="cake">Cake</li>
<li class="cake">Wheat</li>
<li class="cake">Balloons</li>
</ul>
JS
let list = document.querySelectorAll("li");
let items = list.map(elem => {
console.log(elem);
})
querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors. Use array#from to convert NodeList to array then iterate through array#map.
let list = document.querySelectorAll("li");
let items = Array.from(list).map(elem => {
console.log(elem);
})
<ul class="wrapper1" id="testDiv">
<li class="cake">Carrots</li>
<li class="cake">Cake</li>
<li class="cake">Wheat</li>
<li class="cake">Balloons</li>
</ul>
In addition to Itang and Foo's suggestion, you can also use:
[].concat(document.querySelectorAll("li")).map((el) => { console.log(el); })
In fairness I think Foo's suggestion using spread syntax Is probably the most elegant, I'm just not sure how widely the spread operator is supported for NodeLists. (pasted below for reference)
[...document.querySelectorAll('li')].map((el) => { console.log(el); })
If you're using ES6, you can use [...selectors] syntax, like this:
let getMappingList = function (list) {
console.log(typeof list);
for (let item of list) {
console.log(item);
}
console.log("___________________");
list.map(item => console.log(item));
};
getMappingList([...document.querySelectorAll("li")]);
<ul class="wrapper1" id="testDiv">
<li class="cake">Carrots</li>
<li class="cake">Cake</li>
<li class="cake">Wheat</li>
<li class="cake">Balloons</li>
</ul>
After getting the list, we can also use map function, or looping the list using for...of... syntax.
Array(...selectors) is the same way to use:
let getMappingList = function (list) {
console.log(typeof list);
for (let item of list) {
console.log(item);
}
console.log("___________________");
list.map(item => console.log(item));
};
getMappingList(Array(...document.querySelectorAll("li")));
<ul class="wrapper1" id="testDiv">
<li class="cake">Carrots</li>
<li class="cake">Cake</li>
<li class="cake">Wheat</li>
<li class="cake">Balloons</li>
</ul>
I know this is an old thread but listed as #1 on Google search result. So here is an alternative to those in need.
let items = [].map.call(list, item => console.log(item));
I encountered the question of how to use a nodelist (result of document.querySelectorAll) with .map on my project and solved it by using a SPREAD operator to create an array from the nodelist.
Learning as I go so don't hesitate to correct me. It's weird that modern browsers can directly execute a foreach function to a nodelist but not map.
let list = document.querySelectorAll("li");
let items = [...list].map(elem => {
console.log(elem);
})
<ul class="wrapper1" id="testDiv">
<li class="cake">Carrots</li>
<li class="cake">Cake</li>
<li class="cake">Wheat</li>
<li class="cake">Balloons</li>
</ul>
edit : understood how snippets work / SPREAD not REST

JavaScript - Splitting Multiple Return Values

I'm building a filter for a search. Everything worked fine before since we only allowed 1 filter at a time. So I'd get a return like this:
var returnVal = "&filterName=filterProperty"
var filterFields = returnVal.split(['=']);
var filterCategory = filterFields[0];
var filterCatSplit = filterCategory.substr(1);
var filterTitle = filterFields[1];
<h4>filter title</h4>
<ul>
<li>filter one</li>
</ul>
i'd just get the returnVal and split it at the '='
Now we're going to allow multiple values and I'm not sure how to get them all onto the page in a nice list. Now, returnVal can look like this:
var returnVal = "&sizeFilterName=filterProperty,filterPropery&colorFilterName=filterProperty,filterProperty"
I now need this to return like this (or something like this)
<h4>Size Filter Name</h4>
<ul>
<li>Size Filter One</li>
<li>Size Filter Two etc</li>
</ul>
<h4>Color Filter Name</h4>
<ul>
<li>Color Filter One</li>
<li>Color Filter Two etc</li>
</ul>
I've never had to split and slice so many variants before. I'm a bit lost. I hope this is enough to get an idea of what I'm trying to do.
Thanks!
You can pass returnVal to URLSearchParams() then .split() the value by "," perform tasks using the returned array
let returnVal = "&sizeFilterName=filterProperty,filterPropery&colorFilterName=filterProperty,filterProperty";
let params = Array.from([...new URLSearchParams(returnVal)]
, ([key, value]) => [key, value.split(",").filter(Boolean)]);
// do stuff with `params`
params.forEach(([key, value]) =>
document.body.insertAdjacentHTML("beforeend", `<h4>Size ${key}</h4>
<ul>
${value.map(prop => `<li>Color ${prop}</li>`).join("")}
</ul>`)
);

Categories