How to change counter variable from numbers to letters? - javascript

How can I change a counter variable from a number to a letter? Say I have five sections that each read "Section A", "Section B", so on... And I want to change their href attributes as they are being mapped out from "section1" to "sectionA", "section2" to "sectionB", etc?
var sectionNumberLink = 0;
var sectionLetters = assessmentSections.map(function (section) {
sectionNumberLink++;
return '<div class="assess-sec-link">' + '<a href="section' +
sectionNumberLink + '">' + "Section" + '</a>' + '</div>';
}).join('');

You can use String.fromCharCode() and String.prototype.charCodeAt() to convert the number to a letter
Example:
Warning: This will fail if you have more than 26 sections
function toLetter(number) {
let base = 'A'.charCodeAt(0);
return String.fromCharCode(base - 1 + number);
}
console.log(toLetter(1)); // A
console.log(toLetter(2)); // B
If you need more than 26 sections, a bit more code is required:
function toLetter(num) {
let a = "A".charCodeAt(0);
let result = '';
for (let base = 1, mod = 26; (num -= base) >= 0;base = mod, mod *= 26) {
result = String.fromCharCode(num % mod / base + a) + result;
}
return result;
}
console.log(toLetter(1)); // A
console.log(toLetter(27)); // AA
In your code snippet you could use it like this:
let sectionLetters = assessmentSections.map((section, idx) => {
return `
<div class="assess-sec-link">
Section
</div>`;
}).join('');

You can use index to calculate the alphabet in the .map() method,
//sample
var assessmentSections = ["section1", "section2", "section3"]
var sectionLetters = assessmentSections.map(function (section, index) {
return '<div class="assess-sec-link">' + '<a href="section' +
String.fromCharCode('A'.charCodeAt(0) - 1 + (index+1)) + '">' + "Section" + '</a>' + '</div>';
}).join('');
console.log(sectionLetters)

You could do something like:
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
//As #georg suggested, you could do it like:
for(let c=1;c<=alphabet.length;c++){
console.log("Section "+alphabet[c-1])
}
So, you can call alphabet[number-1] to change NUMBER to CHARACTER.
Remember arrays indexes start from 0, that's why it needs to be number-1.

I would try to structure my HTML so I can use an <ol type="A"> instead of <div>s, so that I get automatic ordering with uppercase letters automatically, without me having to do index-to-letter calculations:
// create dummy data
var assessmentSections = Array.from({ length: 50 }, ( item, i ) => {
return { url: i + 1 };
});
var sections = assessmentSections.map(( section, i ) => {
return `<li class="assess-sec-link">Section ${ i + 1 }</li>`;
});
document.querySelector( 'ol' ).innerHTML = sections.join( '' );
<ol type="A"></ol>

Related

Reading JSON object value with key pattern

My JSON structure is as follows
var data = [
{name:'bracket', start_45641654:'46513489431',end_26441:'75434'},
{ name: 'notation', end_746413: '2146464', start_51345641: '76542464' },
];
I want to print start, end object values, Here a random number is appending to keys start_ and end_. Tried to use ^ regular expression pattern but it is not working. Is there any other way to print the values?
data.forEach(function (v, i) {
$('tr').prepend('<td>Name:' + v['name'] + '</td>' +
'<td>Start:' + v['^start_'] + '</td>' +
'<td>End:' + v['^end_'] + '</td>'
);
});
You can't use regular expressions there.
You can loop through the properties of the object, checking if the name has the desired prefix.
data.forEach(function(v) {
let start, end;
Object.entries(v).forEach(([key, val]) => {
if (key.startsWith('start_')) {
start = val;
} else if (key.startsWith('end_')) {
end = val;
}
});
$('tr').prepend('<td>Name:' + v.name + '</td>' +
'<td>Start:' + start + '</td>' +
'<td>End:' + end + '</td>'
);
});
var data = [
{name:'bracket', start_45641654:'46513489431',end_26441:'75434'},
{ name: 'notation', end_746413: '2146464', start_51345641: '76542464' },
];
data.forEach(function (v, i) {
var start = Object.keys(v).find((name) => /start/.test(name));
var end = Object.keys(v).find((name) => /end/.test(name));
console.log(start+" "+end);
$('tr').prepend('<td>Name:' + v['name'] + '</td>' +
'<td>Start:' + v[start] + '</td>' +
'<td>End:' + v[end] + '</td>'
);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr></tr>
</table>
Check this. I don't know if accessing object keys using regex and indexing works. But this should work for you.
Note: I write it on the fly you may need to tweak it
for(let item of data) { // iterate through array
for(let key in item) { // iterate through object keys
if(!key.hasOwnProperty()) // you may wont need it, it makes sure the key you are bringing aren't inherited and the current object is owner of them, mostly used with arrays
return
if(key.indexof('start_') >= 0) // which should be 0 if is ok and -1 if not ok
{
// your code for start
// to get value: item[key]
}
if(key.indexof('end_') >= 0) // which should be 0 if is ok and -1 if not ok
{
// your code for end
// to get value: item[key]
}
}
}
Note: that you may read your JSON file/resource as string, not an object (depend on method used), in that case use JSON.parse(<yourStringJson>)
You can achieve this using reduce function of array. You don't need to use for, for each loop and condition related logic.
For example:
const data = [
{ name: "bracket", start_45641654: "46513489431", end_26441: "75434" },
{ name: "notation", end_746413: "2146464", start_51345641: "76542464" },
];
console.log(data);
const startEndData = data.reduce((p, c) => {
const { name, ...rest } = c;
return [...p, rest];
}, []);
console.log(startEndData);

Trying to loop through large string to find matching substrings

I have one large string with '----begin----' and '----end----' through out the string. I am trying to seperate out each message and display them all inside a div as seperate messages. The following code gets me the first one but I am struggling with the logic to loop through a large string with many messages. How do I loop through the entire large string? Thank you.
var app = document.querySelector('#app');
function parseStr(str) {
var start_idx = str.indexOf('------Begin Message------');
var end_idx = str.indexOf('------End Message------');
app.innerHTML += '<p>' + str.substring(start_idx, start_idx + 27) + '</p>' + '<p>' +
str.substring(start_idx + 27, end_idx) + '</p><p>' +
str.substring(end_idx, end_idx + 23);
}
parseStr(str);
Below code will replace all your header and footer message text to <p> </p> tags giving you back a complete html string.
function parseStr(str) {
let beginMsg = "------Begin Message------";
let endMsg = "------End Message------";
var re1 = new RegExp(beginMsg, "gi");
var re2 = new RegExp(endMsg, "gi");
str = str.replace(re1, "<p>").replace(re2, "</p>");
return str;
}
OR if you want it this way
function parseStr(str) {
let beginMsg = "------Begin Message------";
let endMsg = "------End Message------";
var re1 = new RegExp(beginMsg, "gi");
var re2 = new RegExp(endMsg, "gi");
str = str.replace(re1, "<div><p>"+beginMsg+"</p><p>").replace(re2, "</p><p>"+endMsg+"</p></div>");
return str;
}
This while-loop should go through all messages:
function parseStr(str) {
let beginMsg = "------Begin Message------"
let endMsg = "------End Message------"
while ((let end_idx = str.indexOf(endMsg)) !== -1) {
let start_idx = str.indexOf(beginMsg);
/* start of your code */
app.innerHTML += '<p>' +
str.substring(start_idx, start_idx + beginMsg.length) +
'</p><p>' + str.substring(start_idx + beginMsg.length, end_idx) +
'</p><p>' + str.substring(end_idx, end_idx + endMsg.length);
/* end of your code */
str = str.slice(end_idx + endMsg.length);
}
}
"Large string" is a kind of trigger word for programmers. It matters if we're thinking megabytes, or just a few pages of text. In the "large, but not crazy large" case, just split on the delimiters.
const bigString = "------Begin Message------This is a message------End Message------------Begin Message------This is another message------End Message------"
const startDelim = "------Begin Message------"
const endDelim = "------End Message------"
// use a regex with a conjunction start or ("|") end delimiter
let regex = new RegExp(`${startDelim}|${endDelim}`);
// split, then filter for the messages, processing each as a <p>
let messages = bigString.split(regex).reduce((acc, el, i) => {
if (i%2) acc.push(`<p>${el}</p>`)
return acc
}, [])
console.log(messages)
If it's truly large, you might not want it in memory, and you might not want all of the parsed pieces all in the dom at once.

javascript logic to append/prepend div on html template

I wrote a very basic web app that pulls recipe data back from an API. The data is rendered via being pushed to an html template defined in the javascript file. The layout is controlled via a float-grid in CSS.
The code portion that renders the result and pushes to the template:
function displayRecipeSearchData(data) {
var results = ' ';
if (data.hits.length) {
data.hits.forEach(function(item) {
results += template.item(item);
});
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
The html template through which responses are displayed:
const template = {
item: function(item) {
return '<div class ="col-4">' +
'<div class ="result">' +
'<div class="recipelabel">' +
'<div class="reclist">' + item.recipe.ingredientLines + '</div><!-- end reclist -->' +
'<p class="label">' + item.recipe.label + '</p>' +
'<div class="thumbnail">' +
'<a href="'+ httpsTransform(item.recipe.url) + '" target="_blank">' +
'<img src="' + item.recipe.image + '"alt="' + item.recipe.label + '">' +
'</a>' +
'<div class="recipesource">' +
'<p class="source">' + item.recipe.source + '</p>' +
'</div><!-- end recipesource -->' +
'</div><!-- end thumbnail -->' +
'</div><!-- end recipelabel -->' +
'</div><!-- end result -->' +
'</div><!-- end col-4 -->';
}
};
I am trying to change the logic in the displayRecipeSearchData function such that, for each group of three results, a <div></div> surrounds the block of three results. This is so the rows/columns always work in the flex grid. I have tried several ways but have yet to get the syntax/logic correct. Would an if statement nested in the existing statement be effective?
if(i % 3 === 0 ){ results. += '<div class="row">''</div>'}
Any suggestions would be appreciated.
You could use another variable for storing one row of HTML:
function displayRecipeSearchData(data) {
var results = ' ', row = '';
if (data.hits.length) {
data.hits.forEach(function(item, i) {
row += template.item(item);
if (i % 3 == 2) { // wrap row and add to result
results += '<div class="row">' + row + '</div>';
row = '';
}
});
if (row.length) { // flush remainder into a row
results += '<div class="row">' + row + '</div>';
}
}
else {
results += '<p> No results </p>';
}
$('#js-search-results').html(results);
}
you are definitely doing this the hard way in my opinion.
instead of manually writing the template as a string and trying to inject the string at the right place (potentially creating invalid html) you should use javascripts built-in element creation. also it'll be more modular to create children in their own functions. It will also be much easier to use a function instead of an object to hold your object creator. My version may have a lot more code, but it will be much easier to modify in the long run
const Itemizer = function(){
this.items = [];
const createEl = function(elType, classes, attributes, text, html){
let el = document.createElement(elType)
for(let i = 0; i < classes.length; i++){
el.classList.add(classes[i]
}
for(let attr in attributes){
el.setAttribute(attr, attributes[attr])
}
if(text){
el.innerText = text
}
if(html){
el.innerHTML = html
}
return el
};
const createThumbnail = function(url, image, alt, source){
let thumbnail = createEl("DIV", ["thumbnail"]),
link = createEl("A", [], {href: httpsTransform(url)}),
img = createEl("IMG", [], {src: image, alt: label});
rSource = createRecipeSource(source)
link.appendChild(img);
thumbnail.appendChild(link);
thumbnail.appendChild(rSource)
return thumbnail
};
const createRecipeSource = function(source){
let wrapper = createEl("DIV", ["recipe-source"]);
wrapper.appendChild(createEl("P", ["source"], {}, source))
return wrapper
}
const createRecipeLabel = function({
recipe: {
ingredientLines,
label,
url,
source
}
}){
let labelWrapper = createEl("DIV", ["recipe-label"),
ingredients = createEl("DIV", ["rec-list"], {}, false, ingredientLines),
recipeLabel = createEl("P", ["label"], {}, label),
thumbnail = createThumbnail(url, image, label, source)
labelWrapper.appendChild(ingredients)
labelWrapper.appendChild(recipeLabel)
labelWrapper.appendChild(thumbnail)
return labelWrapper
}
const createNewItem = function(data){
let columnWrapper = createEl("DIV", ["col-4"]),
result = createEl("DIV", ["result"]),
label = createRecipeLabel(data)
columnWrapper.appendChild(result)
result.appendChild(label)
this.items.push(columnWrapper)
return columnWrapper
}.bind(this)
const getItems = function(){
return this.items
}.bind(this)
const getRows = function(){
const rows = []
let row;
for(let i = 0; i < this.items.length; i++){
const item = this.items[i]
if(i % 3 === 0){
row = createEl("DIV", ["row"])
rows.push(row)
}
row.appendChild(item)
}
return rows;
}.bind(this)
return {
add: createNewItem,
get: getItems,
rows: getRows
}
}
You can then use the function like so:
const template = new Itemizer()
function displayRecipeSearchData(data) {
let rows
if (data.hits.length) {
for(let i = 0; i < data.hits.length; i++){
template.add(data.hits[i])
}
rows = template.rows()
} else {
const p = document.createElement("P")
p.innerText = "No Results")
rows = [p]
}
const resultsWrapper = document.getElementById("js-search-results");
for(let i = 0; i < rows.length; i++){
resultsWrapper.appendChild(rows[i])
}
}
it's also good form to separate css classes with hyphens, so I replaced a few of your class names to reflect that
It's also important to note that you don't actually need more than 1 row. if you wrap all of your items in one row section columns will automatically overflow to the next row when they hit the grid limit
My last note is never use target blank. it goes against proper UX, and creates security holes in your application. if your users need to open in a new tab they can hold ctrl or click "open in new tab"

How to colorize a string with hex colors in Javascript

I'd like to extract hex colors from a string and generate a colored html code. Example: I have this string :
#ff0000He#ffccccllo
it'd convert it to
<span style="color:#ff0000">He</span><span style="color:#ffcccc">llo</span>
Single-liner with regex replace:
"#ff0000He#ffccccllo#ffccccooo".replace(/(#.{6})([^#]*)/g, '<span style="color: $1">$2</span>')
Fiddle
You could make it using 'split' with a RegExp
Something like:
var str = '#ff0000He#ffccccllo#123123sdfsdfsdfsdf#AA6456asdasdasd';
var expR = /(#[0-9|a-f]{6})/gi
var a = str.split(expR);
a.shift();
var output = ''
for (var i = 0; i < a.length; i++) {
var color = a[i];
var text = a[++i];
output += '<span style="color: ' + color + '">' + text + '</span>';
};
document.getElementById("result").innerHTML = output;
<div id="result">
</div>
This will do the trick:
var input = '#ff0000He#ffccccllo'.split('#');
var output = '';
input.filter(function (str) {
return !!str;
}).forEach(function (str) {
output += '<span style="#' + str.slice(0, 6) + '">' + str.slice(6) + '</span>';
})
console.log(output) // --> <span style="...
Or as a function:
function convert(input) {
input = input.split('#');
var output = '';
input.filter(function (str) {
return !!str;
}).forEach(function (str) {
output += '<span style="#' + str.slice(0, 6) + '">' + str.slice(6) + '</span>';
})
return output;
}
UPDATE: fixed error in the function dfn. Works now.

How to setup if-statement with multiple conditions, which uses the valid condition's variable in the if-statement?

Okay, that title will sound a bit crazy. I have an object, which I build from a bunch of inputs (from the user). I set them according to their value received, but sometimes they are not set at all, which makes them null. What I really want to do, it make an item generator for WoW. The items can have multiple attributes, which all look the same to the user. Here is my example:
+3 Agility
+5 Stamina
+10 Dodge
In theory, that should just grab my object's property name and key value, then output it in the same fashion. However, how do I setup that if-statement?
Here is what my current if-statement MADNESS looks like:
if(property == "agility") {
text = "+" + text + " Agility";
}
if(property == "stamina") {
text = "+" + text + " Stamina";
}
if(property == "dodge") {
text = "+" + text + " Dodge";
}
You get that point right? In WoW there are A TON of attributes, so it would suck that I would have to create an if-statement for each, because there are simply too many. It's basically repeating itself, but still using the property name all the way. Here is what my JSFiddle looks like: http://jsfiddle.net/pm2328hx/ so you can play with it yourself. Thanks!
EDIT: Oh by the way, what I want to do is something like this:
if(property == "agility" || property == "stamina" || ....) {
text = "+" + text + " " + THE_ABOVE_VARIABLE_WHICH_IS_TRUE;
}
Which is hacky as well. I definitely don't want that.
if(['agility','stamina','dodge'].indexOf(property) !== -1){
text = "+" + text + " " + property;
}
If you need the first letter capitalized :
if(['agility','stamina','dodge'].indexOf(property) !== -1){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
UPDATE per comment:
If you already have an array of all the attributes somewhere, use that instead
var myatts = [
'agility',
'stamina',
'dodge'
];
if(myatts.indexOf(property) !== -1){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
UPDATE per next comment:
If you already have an object with the attributes as keys, you can use Object.keys(), but be sure to also employ hasOwnProperty
var item = {};
item.attribute = {
agility:100,
stamina:200,
dodge:300
};
var property = "agility";
var text = "";
if(Object.keys(item.attribute).indexOf(property) !== -1){
if(item.attribute.hasOwnProperty(property)){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
}
Fiddle: http://jsfiddle.net/trex005/rk9j10bx/
UPDATE to answer intended question instead of asked question
How do I expand the following object into following string? Note: the attributes are dynamic.
Object:
var item = {};
item.attribute = {
agility:100,
stamina:200,
dodge:300
};
String:
+ 100 Agility + 200 Stamina + 300 Dodge
Answer:
var text = "";
for(var property in item.attribute){
if(item.attribute.hasOwnProperty(property)){
if(text.length > 0) text += " ";
text += "+ " + item.attribute[property] + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
}
It's unclear how you're getting these values an storing them internally - but assuming you store them in a hash table:
properties = { stamina: 10,
agility: 45,
...
}
Then you could display it something like this:
var text = '';
for (var key in properties) {
// use hasOwnProperty to filter out keys from the Object.prototype
if (h.hasOwnProperty(k)) {
text = text + ' ' h[k] + ' ' + k + '<br/>';
}
}
After chat, code came out as follows:
var item = {};
item.name = "Thunderfury";
item.rarity = "legendary";
item.itemLevel = 80;
item.equip = "Binds when picked up";
item.unique = "Unique";
item.itemType = "Sword";
item.speed = 1.90;
item.slot = "One-handed";
item.damage = "36 - 68";
item.dps = 27.59;
item.attributes = {
agility:100,
stamina:200,
dodge:300
};
item.durability = 130;
item.chanceOnHit = "Blasts your enemy with lightning, dealing 209 Nature damage and then jumping to additional nearby enemies. Each jump reduces that victim's Nature resistance by 17. Affects 5 targets. Your primary target is also consumed by a cyclone, slowing its attack speed by 20% for 12 sec.";
item.levelRequirement = 60;
function build() {
box = $('<div id="box">'); //builds in memory
for (var key in item) {
if (item.hasOwnProperty(key)) {
if (key === 'attributes') {
for (var k in item.attributes) {
if (item.attributes.hasOwnProperty(k)) {
box.append('<span class="' + k + '">+' + item.attributes[k] + ' ' + k + '</span>');
}
}
} else {
box.append('<span id="' + key + '" class="' + item[key] + '">' + item[key] + '</span>');
}
}
}
$("#box").replaceWith(box);
}
build();
http://jsfiddle.net/gp0qfwfr/5/

Categories