How to remove duplicates from a string except it's first occurence - string

I've been given the following exercise but can't seem to get it working.
//Remove duplicate characters in a
// given string keeping only the first occurrences.
// For example, if the input is ‘tree traversal’
// the output will be "tre avsl".
// ---------------------
var params = 'tree traversal word';
var removeDuplicates = function (string) {
return string;
};
// This function runs the application
// ---------------------
var run = function() {
// We execute the function returned here,
// passing params as arguments
return removeDuplicates;
};
What I've done -
var removeDuplicates = function (string) {
var word ='';
for(var i=0; i < string.length; i++){
if(string[i] == " "){
word += string[i] + " ";
}
else if(string.lastIndexOf(string[i]) == string.indexOf(string[i]))
{
word += string[i];
}
}
return word;
};
I'm not allowed to use replaceAll and when I create an inner for loop it doesn't work.

<script>
function removeDuplicates(string)
{
var result = [];
var i = null;
var length = string.length;
for (i = 0; i < length; i += 1)
{
var current = string.charAt(i);
if (result.indexOf(current) === -1)
{
result.push(current);
}
}
return result.join("");
}
function removeDuplicatesRegex(string)
{
return string.replace(/(.)(?=\1)/g, "");
}
var str = "tree traversal";
alert(removeDuplicates(str));
</script>

First of all, the run function should be returning removeDuplicates(params), right?
You're on the right lines, but need to think about this condition again:
else if(string.lastIndexOf(string[i]) == string.indexOf(string[i]))
With i = 0 and taking 'tree traversal word' as the example, lastIndexOf() is going to be returning 5 (the index of the 2nd 't'), whereas indexOf() will be returning 0.
Obviously this isn't what you want, because 't' hasn't yet been appended to word yet (but it is a repeated character, which is what your condition does actually test for).
Because you're gradually building up word, think about testing to see if the character string[i] exists in word already for each iteration of your for loop. If it doesn't, append it.
(maybe this will come in handy: http://www.w3schools.com/jsref/jsref_search.asp)
Good luck!

Related

LC: Concatenated Words

LC Question: https://leetcode.com/problems/concatenated-words/
var findAllConcatenatedWordsInADict = function(words) {
let m = new Map(), memo = new Map();
let res = [];
for (let i = 0; i < words.length; i++) {
m.set(words[i], 1);
}
for (let i = 0; i < words.length; i++) {
if (isConcat(words[i], m, memo)) res.push(words[i]);
}
return res;
};
function isConcat(word, m, memo) {
if (memo.has(word)) return memo.get(word);
for (let i = 1; i < word.length; i++) {
let prefix = word.slice(0, i);
let suffix = word.slice(i);
if (m.has(prefix) && (m.has(suffix) || isConcat(suffix, m, memo))) {
memo.set(word, true);
return true;
}
}
memo.set(word, false);
return false;
};
Still trying to wrap my head around the solution why do we call the isConcat function ONLY on the suffix that we generate from words[i] and not prefix?
Further, I have tried running various test cases ["cat","cats","dog","dogcatsdog","rat","ratcatdogcat"].
It seems like we do not call the isConcat function on the first suffix that is generated from word[0] ('at'). However, we do seem to call it on 'catsdog' as part of the "dogcatsdog" word so I'm not sure how the logic works as to how it chooses which suffixes to call on....
My mistake was misreading the code and assuming that the following conditions had to return true for a valid concatenated word:
Map has both prefix and suffix; OR
isConcat(suffix) returns true
In fact, prefix is already known to be in map (since map.has(prefix) must be true in order to reach the recursive call in the first place!). Thus, a concatenated word is only valid if the suffix is also an element of map or it's another concatenated word. map.has(suffix) tests if the suffix is in map, while isConcat(suffix, map, memo) tests whether the suffix is also a concatenated word.

How to manipulate a string representing a raw number (e.g. 130000.1293) into a formatted string (e.g. 130,000.13)?

In apps script I want to obtain formatted 'number' strings. The input is an unformatted number. With an earlier answer posted by #slandau, I thought I had found a solution by modifying his code (see code snippet). It works in codepen, but not when I am using apps script.
1. Does anyone know what went wrong here?
2. I noticed this code works except when entering a number ending in .0, in that case the return value is also .0 but should be .00. I would like some help fixing that too.
Thanks!
I have tried to look for type coercion issues, but wasn't able to get it down. I am fairly new to coding.
function commaFormatted(amount)
{
var delimiter = ","; // replace comma if desired
var a = amount.split('.', 2);
var preD = a[1]/(Math.pow(10,a[1].length-2));
var d = Math.round(preD);
var i = parseInt(a[0]);
if(isNaN(i)) { return ''; }
var minus = '';
if(i < 0) { minus = '-'; }
i = Math.abs(i);
var n = new String(i);
var a = [];
while(n.length > 3)
{
var nn = n.substr(n.length-3);
a.unshift(nn);
n = n.substr(0,n.length-3);
}
if(n.length > 0) { a.unshift(n); }
n = a.join(delimiter);
if(d.length < 1) { amount = n; }
else { amount = n + '.' + d; }
amount = minus + amount;
return amount;
}
console.log(commaFormatted('100000.3532'))
The expected result would be 100,000.35.
I am getting this in the IDE of codepen, but in GAS IDE is stops at the .split() method => not a function. When converting var a to a string = I am not getting ["100000", "3532"] when logging var a. Instead I am getting 100000 and was expecting 3532.
Based on this answer, your function can be rewritten to
function commaFormatted(amount)
{
var inputAmount;
if (typeof(amount) == 'string') {
inputAmount = amount;
} else if (typeof(amount) == 'float') {
inputAmount = amount.toString();
}
//--- we expect the input amount is a String
// to make is easier, round the decimal part first
var roundedAmount = parseFloat(amount).toFixed(2);
//--- now split it and add the commas
var parts = roundedAmount.split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
console.log(commaFormatted(100000.3532));
console.log(commaFormatted('1234567.3532'));

I would like my bot to delete the message that contains a keyword or that contains similar characters

in my bot I have implemented a keyword filter that the bot reviews in each message that is written in the chat, until now it works, but I want to improve it, for reasons of respect I will not put words here, so I will put some others example,
The bot detects if you write for example "vulgar", "badword", "hello"
But what I want to achieve is to detect if they write "hellooo", "vuulgarr", vulg4rr"
This is my base where I have the words stored:
badwords.js
var words = ["vulgar", "vulg4r", "hello", "badword4", "badword5"]
module.exports = words;
This is my function that checks if a bad word comes on the way, split any words and then deletes the message if it finds a result, with indexOf()
index.js
const _ = require('lodash');
const badwords = require('./badwords');
/**
* Functions
*/
// compares every word to badWords array from badWords.js
function checkWord(word) {
return badwords.indexOf(word) > -1;
}
/**
* Main Module
*/
module.exports = function (self, nick, channel, message) {
'use strict';
message = message.toLowerCase();
message = message.split(' ');
nick = nick;
channel = channel.toLowerCase();
for (var i = 0, len = message.length; i < len; i++) {
if (checkWord(message[i])) {
self.send('.ban', channel, nick);
}
}
}
Any idea to improve it?, thank's
A more complicated method
We can have two pointers on both strings to compare, but skipping offsets upon duplicates:
function checkString(message, keyword) {
while(message.length > 0) {
if(checkPrefix(message, keyword)) return true
message = message.substr(1)
}
}
function checkPrefix(message, keyword) { // keyword is one of the keywords
let om = 0, ok = 0
while (true) {
if (ok >= keyword.length)
return true // we have finished reading keyword, and everything matched
if(om >= message.length)
return false // message is shorter than keyword
while (om + 1 < message.length && message.charAt(om) === message.charAt(om + 1))
om++ // skip consecutive repetitions in message
while (ok + 1 < keyword.length && keyword.charAt(ok) === keyword.charAt(ok + 1))
ok++ // skip consecutive repetitions in keyword
if (message.charAt(om) !== message.charAt(ok)) return false // encountered an inconsistent character
}
}
A simpler method
Just scan the repetitions in a string and delete them first.
function removeDuplicates(string) {
for (let i = 0; i < string.length - 1; ) {
if (string.charAt(i) === string.charAt(i + 1)) {
string = string.substr(0, i) + string.substr(i + 1) // skip string[i]
} else {
i++ // not duplicate, proceed to next pair
}
}
}
Then you can compare directly:
removeDuplicates(message).indexOf(removeDuplicates(keyword)) !== -1
You can apply it like this:
for (const part in message.split(" ")) {
for (word in words) {
if (removeDuplicates(part).indexOf(removeDuplicates(word)) !== -1)
self.send(".ban", ...)
break
}
}

execute variable number of mongo queries in node, return single result

Ooof. Ever have one of those days where you know you're close, but you just can't quite get it?
I am writing a hangman puzzle solver. This is running in a service written with node/hapi backended with mongo db.
So I have a function:
solvePuzzle(puzzle, alreadyCalled);
The args are the puzzle itself, with solved letters as literals, and unsolved as ?s, like so:
?O?N? ?O ?H? ?TO??
and alreadyCalled being simply a list of letters called but incorrect. After some mucking about, a RegEx is created for each word, which is then sent to a function that queries a wordlist stored in mongo for matches.
Everything is functioning as it should, and if I create a dummy wordlist as a simple array, everything works fine and I get a list of matches.
The return format is an array of objects like so: (I use array indices to preserve word order when displaying possible solutions)
matches[0][?O?N?] = ['GOING', 'DOING', 'BOING'];
So on to the actual PROBLEM. I split the whole puzzle into words, and run a for loop over them, calling the function which performs the mongo query for each one. Problem is, that function call seems to be returning before the query has actually run. The console logs interspersed throughout seem to bear this theory out.
I tried having the query function return a promise, but that has only served to muddy the waters further. I feel like I'm close but yeah - I dunno. Here was my original non-promise code:
function solvePuzzle(puzzle, called) {
// first build the exclusion match pattern
//console.log('solvePuzzle: building match pattern');
var x = buildMatchPattern(puzzle, called);
// split the puzzle into words
//console.log('solvePuzzle: tokenizing puzzle');
var words = tokenize(puzzle.toUpperCase());
//console.log('solvePuzzle:', words);
var results = [];
for(var i = 0; i < words.length; i++) {
console.log('solvePuzzle: matching ' + words[i]);
results[i] = {};
results[i][words[i]] = matchWord(words[i], x);
}
console.log('solvePuzzle: matches: ', results);
return results;
}
function matchWord(word, exclude) {
var pattern = '^';
var letters = word.toUpperCase().split('');
var matches = new Array();
var query = {};
//console.log('matchWord:', letters);
for(var i = 0; i < letters.length; i++) {
if(letters[i] !== '?') {
pattern += letters[i];
}
else {
pattern += exclude;
}
}
pattern += '$';
var re = new RegExp(pattern);
//console.log('matchWord:', re);
query.word = {"$regex" : re, "$options": "i"};
//console.log("matchWord query:", JSON.stringify(query));
db.wordlist.find(query, function (err, words) {
if(err) {
console.error('error:', err);
}
for(let i = 0; i < words.length; i++) {
if(words[i] !== null) {
console.log('loop:', words[i].word);
matches.push(words[i].word);
}
}
console.log('matchWord:', matches.length);
if(matches.length < 1) {
console.log('matchWord: found no matches');
matches.push('No Matches Found');
}
return matches;
});
}
So my console output was basically:
solvePuzzle: matching ?O?N?
solvePuzzle: matches: [] <---- problem
loop: 'going'
loop: 'doing'
etc etc.
.
.
matchWord: 5 (number of matches found);
So as you can see, the call to matchWord is returning before the actual query is running. So I have never done a hapi service backed by mongo. How can I structure this code so it loops over all the words, queries mongo for each one, and returns a single array as result?
TIA.
In node, database calls are asynchronous so you can't use return like this.
You need to use Promise (native in node.js)
this code should work :
function solvePuzzle(puzzle, called) {
var results = [];
// first build the exclusion match pattern
var x = buildMatchPattern(puzzle, called);
// split the puzzle into words
var words = tokenize(puzzle.toUpperCase());
// an array to store the words index
var indexes = Array.apply(null, {
length: words.length
}).map(Number.call, Number); // looks like [1, 2, 3, 4, ...]
// create a Promise for each word in words
var promises = indexes.map(function(index) {
return new Promise(function(resolve, reject) {
console.log('solvePuzzle: matching ' + words[index]);
results[index] = {};
var pattern = '^';
var letters = words[index].toUpperCase().split('');
var matches = new Array();
var query = {};
for (var i = 0; i < letters.length; i++) {
if (letters[i] !== '?') {
pattern += letters[i];
} else {
pattern += exclude;
}
}
pattern += '$';
var re = new RegExp(pattern);
query.word = {
"$regex": re,
"$options": "i"
};
db.wordlist.find(query, function(err, wordsRes) {
if (err) {
console.error('error:', err);
reject(err); // if request failed, promise doesn't resolve
}
for (let i = 0; i < wordsRes.length; i++) {
if (wordsRes[i] !== null) {
console.log('loop:', wordsRes[i].word);
matches.push(wordsRes[i].word);
}
}
console.log('matchWord:', matches.length);
if (matches.length < 1) {
console.log('matchWord: found no matches');
matches.push('No Matches Found');
}
results[index][words[index]] = matches;
resolve(); // request successfull
});
});
});
// when all promise has resolved, then return the results
Promise.all(promises).then(function() {
console.log('solvePuzzle: matches: ', results);
return results;
});
}

How to capitalise the first letter of every word in a string

I am using the Flex SDK and able to capitalise the first letter of every word as follows:
textInput.text.toLowerCase().replace(/\b./g,function(...m):String{return m[0].toUpperCase()})
This works fine, however letters after punctuation are also being capitalised, which works in some cases (e.g. O'Neil) but not others (e.g. Connah'S Quay).
I want to have the code only look at letters at the start of a string and letters after a space. Can anyone provide the correct code to use in this instance please?
This snippet might help:
function firstLetterUpperCase(strData:String):String
{
var strArray:Array = strData.split(' ');
var newArray:Array = [];
for (var str:String in strArray)
{
newArray.push(strArray[str].charAt(0).toUpperCase() + strArray[str].slice(1));
}
return newArray.join(' ');
}
//testing
var strs = "Testing cases (e.g. o'Neil) and others (e.g. connah's quay)."
trace(firstLetterUpperCase(strs));
Result is:
//Testing Cases (e.g. O'Neil) And Others (e.g. Connah's Quay).
If you prefer, try this regex:
/(^| )./g
private function capitalise(s:String):String
{
var strArray:Array = s.split(' ');
var newArray:Array = new Array();
for each (var str:String in strArray)
newArray.push(str.charAt(0).toUpperCase()+str.slice(1));
return newArray.join(' ');
}
trace(capitalise("this is a test - o'Neil - connah's quay"));
// Output: This Is A Test - O'Neil - Connah's Quay
var test = "thIS is a test ansWER to stack OVERFlow";
function process(sentence) {
var words = sentence.split(" ");
var processed = '';
for(var i=0; i < words.length; i++) {
processed += words[i].substr(0,1).toUpperCase() +
words[i].substr(1).toLowerCase();
if(i < words.length-1) {
processed += " ";
}
}
return processed;
}
console.log(process(test));
var input = "i aM tHe kiNG";
capitalised = capitalize(input);
function capitalize(input)
{
var splited = input.split(" ");
//console.log(splited);
var output = Array();
for (i in splited)
{
//convert each letter into lower case
var temp = splited[i].toLowerCase();
//Convert the first char upper case and join with the rest letters of word.
temp = temp.charAt(0).toUpperCase() + temp.substring(1);
//store the word in the array
output.push(temp);
}
//join the words
return output.join(" ");
}
The output will be: I Am The King

Resources