Can't get two variables to concatenate - javascript

I'm a newbie, making a little exercise to practice with arrays. I've tried solving this from previous articles but none seem to have the relevant scenario.
I want to randomly generate sentences into paragraphs using phrases from an array. I got the random sentence generation part working fine.
var ipsumText = ["adventure", "endless youth", "dust", "iconic landmark", "spontaneous", "carefree", "selvedge","on the road", "open road", "stay true", "free spirit", "urban", "live on the edge", "the true wanderer", "vintage motorcyle", "american lifestyle", "epic landscape", "low slung denim", "naturaL"];
//a simple function to print a sentence //
var sentence = function (y) {
var printSentence = "";
for (i=0; i<7; i++) {
//random selection of string from array //
var x = Math.floor(Math.random() * 20);
printSentence += y [x] + " ";
}
return printSentence
};
console.log(sentence(ipsumText));
But now I want to be able to add a comma or full stop to the end of the sentence.
Because each word/phrase from the array used in the sentence prints with a space after it, I need to add an extra word with a full stop or comma right after it to avoid the space between them. To do this I created an extra variable
// create a word and full stop to end a sentence//
var addFullstop = ipsumText[Math.floor(Math.random() * ipsumText.length)] + ". ";
var addComma = ipsumText[Math.floor(Math.random() * ipsumText.length)] + ", ";
These variables work on their own how I expect. They print a random word with a comma or full stop right after them.
However now I can't work out how to get them to add to the end of the sentence. I have tried quite a few versions referencing articles here, but I'm missing something, because when I test it, I get nothing printing to the console log.
This is what I have most recently tried.
// add the sentence and new ending together //
var fullSentence = sentence(ipsumText) + addFullstop;
console.log(fullSentence)
Can someone explain why this wouldn't work? And suggest a solution to try?
thanks

See ES6 fiddle: http://www.es6fiddle.net/isadgquw/
Your example works. But consider a different approach which is a bit more flexible. You give it the array of words, how long you want the sentence to be, and if you want an ending to the sentence, pass in end, otherwise, just leave it out and it will not be used.
The first line generates an array of length count which is composed of random indices to be used to index into the words array. The next line maps these indices to actual words. The last line joins all of these into a sentence separated by a single space, with an optional end of the sentence which the caller specifies.
const randomSent = (words, count, end) =>
[...Array(count)].map(() => Math.floor(Math.random() * words.length))
.map(i => words[i])
.join(' ') + (end || '')
randomSent (['one','two','x','compound word','etc'], 10, '! ')
// x x one x compound word one x etc two two!
To make it more flexible, consider making a function for each task. The code is reusable, specific, and no mutable variables are used, making it easy to test, understand, and compose however you like:
const randInt = (lower, upper) =>
Math.floor(Math.random() * (upper-lower)) + lower
const randWord = (words) => words[randInt(0, words.length)]
const randSentence = (words, len, end) =>
[...Array(len)].map(() => randWord(words))
.join(' ') + (end || '')
const randWordWithEnd = (end) => randWord(ipsumText) + end
const randWordWithFullStop = randWordWithEnd('. ')
const randWordWithComma = randWordWithEnd(', ')
// Now, test it!
const fullSentWithEnd = randSentence(ipsumText, 8, '!!')
const fullSentNoEnd = randSentence(ipsumText, 5)
const fullSentComposed = fullSentNoEnd + randWordWithFullStop
Link again for convenience: http://www.es6fiddle.net/isadgquw/

Related

How to split a string and then split the returned value again connected to another value that has to be split, and join the remainder to the remaining

What I want is something like a string that is around 2000 to 3000 characters with over a hundred non-uniformly located \n in them, and I want to split them for every 1000 characters, and then in the returned strings in an array, for each of the values in the returned array, I want to end the string at the last \n (leaves it as it is if the string contains no \n) and the remainder of the string after the last \n should be appended to the beginning of the next value in the array, and then this should be carried out AFTER the previous string has been fixed to the last \n
I hope you understand what I mean, and this is my code
module.exports={
async split(text,length,max){
if (text.length > max){
return;
}
let regex = new RegExp(`.{1,${length}}`, "g");
let splitted = text.match(regex);
return splitted;
}
}
The place where its fetched and executed is here:
let splitted = await split(lyrics,1000,6000)
I managed to do the split for every 1000 words but the thing that I explained at the top is what I want to do and I am unable to manage it, so can anyone help out?
EDIT: Suppose I wanted to split the string for a max of 20 characters with the max string length of 1000, and if it bypasses that limit, then it means that nothing will be returned. It can do that second stage of splitting (as I mentioned in the question as \n) with a whitespace ( ).
Imagine that the string is: Hello, I love Stack Overflow, and it is super cool
In my current code, if we did
let string = `Hello, I love Stack Overflow, and it is super cool`
let splitted = await split(string, 10, 1000)
It would return
["Hello, I l", "ove Stack ", "Overflow, ", "and it is ", "super cool"]
What if another agument is added in split(), making it:
async split(text, length, max, splitAt)
splitAt can mean \n or depending on choice
The result that I want to return is: ["Hello, I", "love Stack", "Overflow,", "and it is", "super cool"]
The thing is, I can not understand how to do it
You certainly don't need this method to be async and it should just be a case of stepping through the string, splitting by len and finding the lastIndexOf your splitAt argument, and then taking that chunk into an array using substring
Something like this:
function split(text, len, max, splitAt) {
if (text.length > max) {
return;
}
let pos = 0;
const chunks = []
while (pos < text.length) {
if (pos + len >= text.length) {
chunks.push(text.substring(pos));
pos = text.length
} else {
const s = text.substring(pos, pos + len + 1).lastIndexOf(splitAt);
chunks.push(text.substring(pos, pos + s));
pos += s + 1;
}
}
return chunks;
}
let string = `Hello, I love Stack Overflow, and it is super cool`
let splitted = split(string, 10, 1000, " ")
console.log(splitted);
If I understand it correctly, you want to split the text into chunks that have a max size of 1000, and they should end with a newline character.
function split(str, chunkSize){
const chunks = [];
let current_chunk = ""
str.split("\n").forEach(part => {
if(current_chunk.length + part.length > chunkSize){
// Adds the chunk to chunks and resets current chunk.
chunks.push(current_chunk);
current_chunk = "";
}
// adds \n to the start of the part, if the current chunk isn't empty.
current_chunk.length
? current_chunk += `\n${part}`
: current_chunk += part
})
// Used to get the last one, if it isn't empty.
if(current_chunk.length) chunks.push(current_chunk);
return chunks;
}
Should look something like this. Haven't tested it though, since I've written it on my phone.

Return random json object by length of category

I have a json array with words in different languages that I want to alert randomly. The thing is that I have different number of words in every language, so when I use the code below, I am getting undefined at some cases (because some words has more languages than others, and so the length property is longer than it should be).
I have tried to use all.french.length at the end of line 2, but got an error. Does anybody know what should I do?
Thanks!
all = [{"english":"cat", "french":"chat"}, {"english":"dog"}]
let z = Math.floor(Math.random() * all.length);
alert('random: ' + all[z].french);
Edit: I want it only to alert words only in the language I defined in line 3. In this case, I want only to display french words: only alert "chat" again and again. My original data of words is larger than that, of course, and has more languages, so I can't use 0.
If you just want a random element where the translation french exists you can filter your array
const filteredArray = all.filter(element =>
element.french === "" || element.french);
let z = Math.floor(Math.random() * filteredArray.length);
alert('random: ' + filteredArray[z].french);
you can do that:
const all = [{"english":"cat", "french":"chat"}, {"english":"dog"}]
function myAlert(lang)
{
let arr = all.filter( e=>!!e[lang])
, z = Math.floor(Math.random() * arr.length)
;
alert('random: ' + arr[z][lang] )
}
myAlert('french')
myAlert('english')

Get initials and full last name from a string containing names

Assume there are some strings containing names in different format (each line is a possible user input):
'Guilcher, G.M., Harvey, M. & Hand, J.P.'
'Ri Liesner, Peter Tom Collins, Michael Richards'
'Manco-Johnson M, Santagostino E, Ljung R.'
I need to transform those names to get the format Lastname ABC. So each surename should be transformed to its initial which are appended to the lastname.
The example should result in
Guilcher GM, Harvey M, Hand JP
Liesner R, Collins PT, Richards M
Manco-Johnson M, Santagostino E, Ljung R
The problem is the different (possible) input format. I think my attempts are not very smart, so I'm asking for
Some hints to optimize the transformation code
How do I put those in a single function at all? I think first of all I have to test which format the string has...??
So let me explain how far I tried to solve that:
First example string
In the first example there are initials followed by a dot. The dots should be removed and the comma between the name and the initals should be removed.
firstString
.replace('.', '')
.replace(' &', ', ')
I think I do need an regex to get the comma after the name and before the initials.
Second example string
In the second example the name should be splitted by space and the last element is handled as lastname:
const elm = secondString.split(/\s+/)
const lastname = elm[elm.length - 1]
const initials = elm.map((n,i) => {
if (i !== elm.length - 1) return capitalizeFirstLetter(n)
})
return lastname + ' ' + initals.join('')
...not very elegant
Third example string
The third example has the already the correct format - only the dot at the end has to be removed. So nothing else has to be done with that input.
It wouldn't be possible without calling multiple replace() methods. The steps in provided solution is as following:
Remove all dots in abbreviated names
Substitute lastname with firstname
Replace lastnames with their beginning letter
Remove unwanted characters
Demo:
var s = `Guilcher, G.M., Harvey, M. & Hand, J.P.
Ri Liesner, Peter Tom Collins, Michael Richards
Manco-Johnson M, Santagostino E, Ljung R.`
// Remove all dots in abbreviated names
var b = s.replace(/\b([A-Z])\./g, '$1')
// Substitute first names and lastnames
.replace(/([A-Z][\w-]+(?: +[A-Z][\w-]+)*) +([A-Z][\w-]+)\b/g, ($0, $1, $2) => {
// Replace full lastnames with their first letter
return $2 + " " + $1.replace(/\b([A-Z])\w+ */g, '$1');
})
// Remove unwanted preceding / following commas and ampersands
.replace(/(,) +([A-Z]+)\b *[,&]?/g, ' $2$1');
console.log(b);
Given your example data i would try to make guesses based on name part count = 2, since it is very hard to rely on any ,, & or \n - which means treat them all as ,.
Try this against your data and let me know of any use-cases where this fails because i am highly confident that this script will fail at some point with more data :)
let testString = "Guilcher, G.M., Harvey, M. & Hand, J.P.\nRi Liesner, Peter Tom Collins, Michael Richards\nManco-Johnson M, Santagostino E, Ljung R.";
const inputToArray = i => i
.replace(/\./g, "")
.replace(/[\n&]/g, ",")
.replace(/ ?, ?/g, ",")
.split(',');
const reducer = function(accumulator, value, index, array) {
let pos = accumulator.length - 1;
let names = value.split(' ');
if(names.length > 1) {
accumulator.push(names);
} else {
if(accumulator[pos].length > 1) accumulator[++pos] = [];
accumulator[pos].push(value);
}
return accumulator.filter(n => n.length > 0);
};
console.log(inputToArray(testString).reduce(reducer, [[]]));
Here's my approach. I tried to keep it short but complexity was surprisingly high to get the edge cases.
First I'm formatting the input, to replace & for ,, and removing ..
Then, I'm splitting the input by \n, then , and finally (spaces).
Next I'm processing the chunks. On each new segment (delimited by ,), I process the previous segment. I do this because I need to be sure that the current segment isn't an initial. If that's the case, I do my best to skip that inital-only segment and process the previous one. The previous one will have the correct initial and surname, as I have all the information I neeed.
I get the initial on the segment if there's one. This will be used on the start of the next segment to process the current one.
After finishing each line, I process again the last segment, as it wont be called otherwise.
I understand the complexity is high without using regexp, and probably would have been better to use a state machine to parse the input instead.
const isInitial = s => [...s].every(c => c === c.toUpperCase());
const generateInitial = arr => arr.reduce((a, c, i) => a + (i < arr.length - 1 ? c[0].toUpperCase() : ''), '');
const formatSegment = (words, initial) => {
if (!initial) {
initial = generateInitial(words);
}
const surname = words[words.length - 1];
return {initial, surname};
}
const doDisplay = x => x.map(x => x.surname + ' ' + x.initial).join(', ');
const doProcess = _ => {
const formatted = input.value.replace(/\./g, '').replace(/&/g, ',');
const chunks = formatted.split('\n').map(x => x.split(',').map(x => x.trim().split(' ')));
const peoples = [];
chunks.forEach(line => {
let lastSegment = null;
let lastInitial = null;
let lastInitialOnly = false;
line.forEach(segment => {
if (lastSegment) {
// if segment only contains an initial, it's the initial corresponding
// to the previous segment
const initialOnly = segment.length === 1 && isInitial(segment[0]);
if (initialOnly) {
lastInitial = segment[0];
}
// avoid processing last segments that were only initials
// this prevents adding a segment twice
if (!lastInitialOnly) {
// if segment isn't an initial, we need to generate an initial
// for the previous segment, if it doesn't already have one
const people = formatSegment(lastSegment, lastInitial);
peoples.push(people);
}
lastInitialOnly = initialOnly;
// Skip initial only segments
if (initialOnly) {
return;
}
}
lastInitial = null;
// Remove the initial from the words
// to avoid getting the initial calculated for the initial
segment = segment.filter(word => {
if (isInitial(word)) {
lastInitial = word;
return false;
}
return true;
});
lastSegment = segment;
});
// Process last segment
if (!lastInitialOnly) {
const people = formatSegment(lastSegment, lastInitial);
peoples.push(people);
}
});
return peoples;
}
process.addEventListener('click', _ => {
const peoples = doProcess();
const display = doDisplay(peoples);
output.value = display;
});
.row {
display: flex;
}
.row > * {
flex: 1 0;
}
<div class="row">
<h3>Input</h3>
<h3>Output</h3>
</div>
<div class="row">
<textarea id="input" rows="10">Guilcher, G.M., Harvey, M. & Hand, J.P.
Ri Liesner, Peter Tom Collins, Michael Richards
Manco-Johnson M, Santagostino E, Ljung R.
Jordan M, Michael Jackson & Willis B.</textarea>
<textarea id="output" rows="10"></textarea>
</div>
<button id="process" style="display: block;">Process</button>

Rubik´Cube Scrambling Algorithm - JavaScript

I have been working on a Rubik’s Cube Timer website, and I need to make a scrambling algorithm. I’ll go over how the scrambling algorithm should work:
Each face has it’s own letter, it’s initial. for examble, if you want to move the front face, you would write “ F “. If you want to move the the right face, you would write “ R “, and so on. just note that the bottom face is D, as for down. So you have D U R L B F.
If there is nothing after that letter, you turn it clockwise. If there is an appostrophe “ ‘ “, you turn it counter-clockwise. If there is a 2, you turn it two times. Now the thing is that you cannot have 2 same letters next to oneanother, as they would cancel (For example “.. U U’ ...” would be the same as doing nothing. So far, I have this taken care of in my algorithm.
The problem comes when you have one letter, then it’s opposite, then again the first letter, ( For example “.. U D U’...” (would mean Up clockwise, Down clockwise, Up counterclokwise)).
I have no idea how to check for these and avoid them automatically. Here’s the code:
<div id=“Scramble”></div>
<script>
generateScramble();
function generateScramble() {
// Possible Letters
var array = new Array(" U", " D", " R", " L", " F", " B")
// Possible switches
var switches = ["", "\'", "2"];
var array2 = new Array(); // The Scramble.
var last = ''; // Last used letter
var random = 0;
for (var i = 0; i < 20; i++) {
// the following loop runs until the last one
// letter is another of the new one
do {
random = Math.floor(Math.random() * array.length);
} while (last == array[random])
// assigns the new one as the last one
last = array[random];
// the scramble item is the letter
// with (or without) a switch
var scrambleItem = array[random] + switches[parseInt(Math.random()*switches.length)];
array2.push(scrambleItem); // Get letters in random order in the array.
}
var scramble = "Scramble: ";
// Appends all scramble items to scramble variable
for(i=0; i<20; i++) {
scramble += array2[i];
}
document.getElementById("Scramble").innerHTML = scramble; // Display the scramble
}
</script>
For starters God's Number is 20 for Rubik;s cube so you got only 20 moves instead of 25. I assume you are not doing scrambling (as your title suggest) but instead generate solution command strings for genere&test solver type. There are too many sequences that cancel each other and to check for all of them would be most likely slower than try them out actually.
The problem is that even O(n^20) is huge and you need to lower the 20. That is done by LUT holding semi solved states. For example create table holding states for all combinations of 5 turn scrambling. Then use that as end condition turning your solver into O(n^15 + n^5) = O(n^15) ...

Look for substring in a string with at most one different character-javascript

I am new in programing and right now I am working on one program. Program need to find the substring in a string and return the index where the chain starts to be the same. I know that for that I can use "indexOf". Is not so easy. I want to find out substrings with at moste one different char.
I was thinking about regular expresion... but not really know how to use it because I need to use regular expresion for every element of the string. Here some code wich propably will clarify what I want to do:
var A= "abbab";
var B= "ba";
var tb=[];
console.log(A.indexOf(B));
for (var i=0;i<B.length; i++){
var D=B.replace(B[i],"[a-z]");
tb.push(A.indexOf(D));
}
console.log(tb);
I know that the substring B and string A are the lowercase letters. Will be nice to get any advice how to make it using regular expresions. Thx
Simple Input:
A B
1) abbab ba
2) hello world
3) banana nan
Expected Output:
1) 1 2
2) No Match!
3) 0 2
While probably theoretically possible, I think it would very complicated to try this kind of search while attempting to incorporate all possible search query options in one long complex regular expression. I think a better approach is to use JavaScript to dynamically create various simpler options and then search with each separately.
The following code sequentially replaces each character in the initial query string with a regular expression wild card (i.e. a period, '.') and then searches the target string with that. For example, if the initial query string is 'nan', it will search with '.an', 'n.n' and 'na.'. It will only add the position of the hit to the list of hits if that position has not already been hit on a previous search. i.e. It ensures that the list of hits contains only unique values, even if multiple query variations found a hit at the same location. (This could be implemented even better with ES6 sets, but I couldn't get the Stack Overflow code snippet tool to cooperate with me while trying to use a set, even with the Babel option checked.) Finally, it sorts the hits in ascending order.
Update: The search algorithm has been updated/corrected. Originally, some hits were missed because the exec search for any query variation would only iterate as per the JavaScript default, i.e. after finding a match, it would start the next search at the next character after the end of the previous match, e.g. it would find 'aa' in 'aaaa' at positions 0 and 2. Now it starts the next search at the next character after the start of the previous match, e.g. it now finds 'aa' in 'aaaa' at positions 0, 1 and 2.
const findAllowingOneMismatch = (target, query) => {
const numLetters = query.length;
const queryVariations = [];
for (let variationNum = 0; variationNum < numLetters; variationNum += 1) {
queryVariations.push(query.slice(0, variationNum) + "." + query.slice(variationNum + 1));
};
let hits = [];
queryVariations.forEach(queryVariation => {
const re = new RegExp(queryVariation, "g");
let myArray;
while ((searchResult = re.exec(target)) !== null) {
re.lastIndex = searchResult.index + 1;
const hit = searchResult.index;
// console.log('found a hit with ' + queryVariation + ' at position ' + hit);
if (hits.indexOf(hit) === -1) {
hits.push(searchResult.index);
}
}
});
hits = hits.sort((a,b)=>(a-b));
console.log('Found "' + query + '" in "' + target + '" at positions:', JSON.stringify(hits));
};
[
['abbab', 'ba'],
['hello', 'world'],
['banana', 'nan'],
['abcde abcxe abxxe xbcde', 'abcd'],
['--xx-xxx--x----x-x-xxx--x--x-x-xx-', '----']
].forEach(pair => {findAllowingOneMismatch(pair[0], pair[1])});

Categories

Resources