For loop a number to create buttons inside template literal - javascript

Getting a bit stuck with this one.
I'm looping through an object (dataLayer.Tests) and I'm displaying the values of my content, in a DIV. Here's an example of how this object looks:
I'm doing this by looping through my object with forEach. (And in this example, I'm just console.logging my result result3).
The problem I'm having, is that within my forEach, I want to display create/display buttons, depending on what the number is in the totalVariants key/value.
So for example, if the totalVariants === 2, I want to create 2 buttons. If it is one, I want to create 1 button.
I know I need to for loop through this particular value, but I'm not sure how to do this, within a template literal.
Here's my code.
dataLayer.Tests.forEach((element, index, array) => {
let result3 = '';
let numberOfVariants = element.totalVariants;
if (numberOfVariants >= 1) {
for (i = 0; i < numberOfVariants; i++) {
console.log("The number is ", i + 1);
}
result3 += `
<div class="CRO-variant-result">
<p>Date Launched: ${element.date}</p>
<p>Test ID: ${element.id}</p>
<p>Test Description: ${element.name}</p>
<p>Variant Active: ${element.variant}</p>
<p>Total Variants: ${element.totalVariants}</p>
${(function () {
for (i = 0; i < element.totalVariants; i++) {
return `<button>${i}</button>`
}
})()}
</div>
`
console.log("result3", result3);
};
});
I've seen solutions which include .map and object.keys, but this doesn't seem to work/return anything. (Probably as I just need to loop through a number and not array etc.
Any ideas/pointers, would be appreciated.
Basically, I'm not sure how to loop through a number, within a template literal and return x number of elements.
Thanks,
Reena

numberOfVariants is an number, not an object, so one way you could do this is create a new incrementing array of that length (Array.from(Array(numberOfVariants).keys()) does this nicely) and then map over that array as you're doing.
${Array.from(Array(numberOfVariants).keys()).map(i => (
`<button value="${i}">${i}</button>`
)).join('')}
I'm not quite sure what you want to appear inside the button (maybe the integer of the current number as you increment)?

Related

What does the .filter(i => i) do in this case?

I'm reading the process being built on
https://developers.cardano.org/docs/integrate-cardano/listening-for-payments-cli/#process-utxo-table
and I'm stuck on the results of this part:
// Calculate total lovelace of the UTXO(s) inside the wallet address
const utxoTableRows = rawUtxoTable.data.trim().split('\n');
let totalLovelaceRecv = 0;
let isPaymentComplete = false;
for (let x = 2; x < utxoTableRows.length; x++) {
const cells = utxoTableRows[x].split(" ").filter(i => i);
totalLovelaceRecv += parseInt(cells[2]);
}
I understand that up to the .split(" ") part, the table has been split into at least 2 items within the array of utxoTableRows since the first row the headers that we aren't interested in and the second row is the first utxo being processed. The .split(" ") then splits the row where there is a space but I don't know what the filter part does. The query utxo table looks as follows:
TxHash
TxIx
Amount
dfb99f8f103e56a856e04e087255dbaf402f3801acb71a6baf423a1054d3ccd5
0
1749651926
As you can see there are two rows. I'm completely lost at the filter component. I get more lost in the following command parseInt(cells[2]). I know somehow it takes the amount as a string and converts to integer. The [2] reference throws me off because wouldn't it have to be [3] since the amount is the 3rd item that gets split in that row? The only thing I can think of is that the .filter(i => i) throws out the TxHash, then saves the TxIx (in this case 0) in cell[1] and the amount (in this case 1749651926) in cell[2], both as strings.
Thank you for the help.
filter will filter out all items in the array where the callback returns false after coercion.
In this case, the filter removes all blank strings in the array. If the item is an empty string, the callback returns false, since empty strings, when coerced to a boolean, are false (see: Falsy values).

Adding onto float within json object

So I have a JSON object that is dynamically creates based on lots. I'm trying to get the total cost of each lot. Each object in the list is a different purchase.
var lot_list = [{lot:123456,cost:'$4,500.00'}, {lot:654321, cost:'$1,600.00'}, {lot:123456, cost:'$6,500.00'}]
I want the total cost for each lot so I tried
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
totalBalances[lots[lot]] += parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
This ends with every lot having a null cost
I also tried
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
totalBalances[lots[lot]] = parseInt(totalBalances[lots[lot]]) + parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
Neither of these worked any help is much appreciated.
You cannot get a value to sum with parseFloat('$4,500.00') because of the invalid characters. To remove the dollar sign and commas you can replace using;
> '$4,500.00'.replace(/[^\d\./g,'')
> "4500.00"
Here the regex matches anything that is not a digit or decimal place with the global modifier to replace all occurrances.
You can map your array of objects to the float values using Array.map() to get an array of float values.
> lot_list.map((l) => parseFloat(l.cost.replace(/[^\d\.]/g,'')))
> [4500, 1600, 6500]
With an array of float values you can use Array.reduce() to get the sum;
> lot_list.map((l) => parseFloat(l.cost.replace(/[^\d\.]/g,''))).reduce((a,c) => a+c)
> 12600
EDIT
To get totals for each lot map an object with the lot id included and then reduce onto an object with;
> lot_list
.map((l) => {
return {
id: l.lot,
cost: parseFloat(l.cost.replace(/[^\d\.]/g,''))
}
})
.reduce((a,c) => {
a[c.id] = a[c.id] || 0;
a[c.id] += c.cost;
return a;
}, {})
> { 123456: 11000, 654321: 1600 }
Here the reduce function creates an object as the accumulator and then initialises the sum to zero if the lot id has not been summed before.
based on your code
var totalBalances = {};
function addBalance(){
lot_list.forEach(function(lots){
// totalBalances[123456] is not defined yet, this should gets you problem if you're trying calculate its value
// totalBalances[lots[lot]] += parseFloat(lots['cost'].replace('$','').replace(',',''));
// maybe you should use this instead
totalBalances[lots[lot]] = parseFloat(lots['cost'].replace('$','').replace(',',''));
});
}
but, if you want to count total value of cost, you might considering using array reduce
function countCost(){
return lot_list.reduce((accum, dt) => {
return accum + parseFloat(l.cost.replace(/[^\d\.]/g,''))
}, parseFloat(0));
} //this gonna bring you total count of all cost

How to map API with multiple objects in [Javascript, ES6]

I am working on the domain API I would like to display domain names and prices at the same time but They are in two different object. I always get undefined when I use this. So If I dataArray[0] or dataArray[1] can get all the result only cannot get both. Thank you for the help.
domain.Name inside of data.Domains object
domain.PriceInfo inside of data.Prices object.
const displayDomains = (data) => {
const dataArray = [data.Domains, data.Prices];
const htmlString = dataArray[(0, 1)]
.map((domain) => {
return `<div class="domains">
<h2>${domain.Name}</h2>
<div class="domain-price-container">
<sub class="discount-price">${domain.PriceInfo}</sub>
</div>
</div>`;
})
.join('');
If data.Domains and data.Prices are equal length arrays, then a straight forward workaround would be to simply loop over both arrays with common index,
const dataArray = [data.Domains, data.Prices];
let index = 0;
let htmlString = [];
for(index; index < sizeOfArray; index++) {
htmlString.push(
<div class="domains">
<h2>${dataArray[0][index].Name}</h2>
<div class="domain-price-container">
<sub class="discount-price">${dataArray[1][index].PriceInfo}</sub>
</div>
</div>
);
}
If both arrays have different sizes, insert all common elements upto smaller sizes out of two, and next insert leftover elements.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The second argument passed into the .map callback is the index. That allows you to do something like this:
data.Domains.map((domain, index) => {
const price = data.Prices[index];
return <>; // whatever JSX you want here, including both domain and price information
})`

How map with function and text ReactJs

I would like to explain my problem of the day.
currently i map, everything works correctly
my problem and that I wish I could create a div in it in order to display the result of total
, unfortunately I can't
Do you have an idea of how to fix this?
{this.props.items.map(item => {
const product = this.props.items;
let total = 0;
console.log(total)
console.log('yolo',product)
console.log('yolo array',product[0].quantity)
for (let i = 0 ; i<product.length;i++){
console.log('products',product[i].quantity)
total = total + product[i].quantity;
}
}
)}
even if you were to render something using total, with this method you would render a "total item" for each item in props.items. What you want to use here is the reduce method. Here is an example with a very basic div, but you can decide what to render in place of it
<div>
{this.props.items.reduce((total,item) => item.quantity + total, 0)}
</div>
What the reduce methos does is returning a single value from a list of elements. The second parameter is your initial value, while he first is the callback that will be executed at each iteration

Javascript: Iterate through array and modify items inside the array

I'm trying to set up a function on my page that will iterate through a series of news titles and will strikethrough the title if it contains a specific marker.
The titles are pulled from a WordPress post database and displayed within a <div> with ID="news". Each specific title is a link <a> with class="title".
I am first collecting all the titles into an array so I can iterate through them but for some reason, it doesn't work properly.
My code is:
<script type="text/javascript">
//first I create the array
var test_DOM = document.getElementById("news").getElementsByClassName("title");
//then I set the marker
var marker = "[X]"
//then I wirte the function to iterate through the array://
function checkX() {
for(i in test_DOM){
if (i.includes(marker)) {
i = "<del>", i, "</del>";
console.log(test_DOM[i], "changed")
}
console.log(test_DOM[i].innerHTML)
}
};
//and I call the function
checkX()
An expected result would be to see the list of news to change from
[x] News 1
[EG] News 2
to
[x] News 1 <- struck through
[EG] News 2 <- still the same
However, nothing happens. I know that getElementsByClassName gets me an array of objects and that I should use .innerHTML and some point, but using it inside the function like
function checkX() {
for(i in test_DOM){
if (i.innerHTML.includes(marker)) {
i.innerHTML = "<del>", i, "</del>";
console.log(test_DOM[i], "changed")
}
console.log(test_DOM[i].innerHTML)
}
};
gives an error of "TypeError: i.innerHTML is undefined"
I think I understand what you're trying to do and have a solution for you. There are a few things that need to be updated to get the script working as you described:
Your DOM query can be shortened using document.querySelectorAll.
When using String.includes(), it is case sensitive. This means that [x] is not equal to [X], so be sure to check the correct version for your test.
As #2pha mentioned in their comment, test_DOM will return a NodeList, which is an array-like structure, but not quite an array. To convert it to an array that can be looped over as you intend, you can use [].slice.call(test_DOM).
As #VLAZ mentioned in their comment, the comma operator will not combine strings. You should use the 'string' + 'string' syntax to combine strings.
Also as #VLAZ mentioned, the for...in loop is more intended for objects, in which it will output the keys of the object, not the values, so this isn't appropriate for your use case. You can use Array.forEach() instead.
As you alluded to, the Array.includes() check should use innerHTML for its check, and innerHTML should be used to then reset the value of the element.
I included a snippet below with some modifications.
var test_DOM = document.querySelectorAll('#news .title');
var marker = '[x]';
function checkX() {
[].slice.call(test_DOM).forEach(function(elem, i) {
if(elem.innerHTML.includes(marker)) {
elem.innerHTML = '<del>' + elem.innerHTML + '</del>';
}
});
}
checkX()
<div id="news">
<h1 class="title">[x] News 1</h1>
<h1 class="title">[EG] News 2</h1>
</div>
Follow on from my comment.
ie. "getElementsByClassName() returns a "NodeList" object, not an array as may be expected". But does have a length property.
The minimal changes to your code to get it to work.
//first I create the array
var test_DOM = document.getElementById("news").getElementsByClassName("title");
//then I set the marker
var marker = "[X]"
//console.log(test_DOM);
//then I wirte the function to iterate through the array://
function checkX() {
for(i = 0; i < test_DOM.length; i++){
if (test_DOM[i].innerHTML.includes(marker)) {
test_DOM[i].innerHTML = "<del>" + test_DOM[i].innerHTML + "</del>";
//console.log(test_DOM[i], "changed")
}
//console.log(test_DOM[i].innerHTML)
}
};
//and I call the function
checkX();
<div id="news">
<h2 class="title">title 1</h2>
<h2 class="title">[X]title 2</h2>
<h2 class="title">title 3</h2>
</div>

Categories

Resources