How would I go about splitting a string into characters - Javascript [closed] - javascript

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I want to split a string, such as this:
"Hello $id1, how are you $id2 etc...",
but "$idInt" should not be split:
const test = [
"$id1",
"$id2",
"$id3"
]
let str = "test $id1!";
str = str.split("");
// wanted result: ["t", "e", "s", "t", " ", "$id1", "!"]; 
// actual result: ["t", "e", "s", "t", " ", "$", "i", "d", "1" "!"]
This would take anything from the "test" array and not split it with every sepparate character.
I only searched for other resources but I haven't been able to find something that does what I want this to do exactly.
I also got this code:
let reg = /\%\[a-zA-Z0-9]\:[0-9]+\%/gim;
let yourstring = 'Some thinger %09ff:13%';
let matches = reg.exec(yourstring);
let records = [];
let pieces = [];
if (matches != null) {
for (let [i, match] of Array.from(matches).entries()) {
let start = yourstring.indexOf(match);
let end = match.length + start;
records.push({
start: start,
end: end,
match: match,
});
}
records.sort((a, b) => (a.start > b.start ? 1 : a.start < b.start ? -1 : 0));
if (records[0].start > 0) {
records = [{
start: 0,
end: 0,
match: ""
}].concat(records);
}
for (let i = 1; i < records.length; i++) {
pieces.push(yourstring.slice(records[i - 1].end, records[i].start).replace(......)); // replace goes here
pieces.push(records[i].match);
}
yourstring = pieces.join("")
} else {
yourstring = yourstring.replace(.......) // replace goes here
} 
but it's regex and I want a set of strings to not be replaced rather than this here with regex

I would propose using match instead of split:
let s = "Hello %id:1%, how are you %id:2% etc...";
let result = s.match(/%id:\d+%|./gs);
console.log(result);

You should use the split function like this:
let str1 = "test %id:1%!"
let arr1 = str1.split('%')
let test = [];
// arr1 will become ['test ','id:1','!']
// every alternate one will become one of the usable one.
arr1.forEach((x,index)=>{
if (index%2 == 1) {
// push in test and add the '%'s back in.
test.push(`%${x}%`)
}
})

Trincot's answer is better but I already wrote this so here it is...
var str = "Hello %id:1%, how are you %id:2% etc...";
console.log(splitStr(str));
function splitStr(str) {
var is_var = false;
var chars = [];
var group = '';
for (var i = 0; i < str.length; i++) {
let char = str[i];
if (is_var) {
group += char;
if ('%' == char) {
chars.push(group);
group = '';
is_var = false;
}
} else {
if ('%' == char) {
group += char;
is_var = true;
}else{
chars.push(char);
}
}
}
return chars;
}

Related

create a unique key from 3 Alphabet

I'm trying to create a unique key using the alphabets;
I've managed to work it out but I don't really like the approach I took. and I believe someone out there might be able to help me get a better way to do this code. Please let me know if you think of a better idea? or whatever your idea to compare with mine?
I think this might be a good way to share knowledge .. Thanks in advance
let string = 'ABE';
let ar1 = ["A", "B", "C", "D", "E"];
string = string.split('');
if (string[2] !=='E') {
for (let i = 0; i < 5; i++) {
if (string[2] === ar1[i]) {
i = i+1;
string[2] = ar1[i]
}
}
string = string.toString().replace(/,/g, '');
console.log("case 1", string);
} else if (string[1] !=='E' && string[2] ==='E') {
string[2] ='A';
for (let i = 0; i < 5; i++) {
if (string[1] === ar1[i]) {
i = i+1;
let newString = ar1[i];
console.log(ar1[i]);
string[1] = newString;
}
}
string = string.toString().replace(/,/g, '');
console.log("case 2", string);
} else if (string[1] ==='E' && string[2] ==='E') {
string[1] ='A';
string[2] ='A';
for (let i = 0; i < 5; i++) {
if (string[0] === ar1[i]) {
i = i+1;
string[0] = ar1[i];
}
}
string = string.toString().replace(/,/g, '');
console.log("case 3", string);
}
Results would be like:
if string = "AAA" Then the result "AAB"
if string = "AAE" Then result will be "ABA"
if string = "AEE" Then result will be "BAA"
You could encode the value to a numerical value, like this palce value
36 6 1
C B A
2 1 0 78
and encode an incremented value with the wanted characters and length.
function decode(value, characters) {
const
values = Object.fromEntries(Array.from(characters, (v, i) => [v, i]));
return value
.split('')
.reduce((s, v) => s * characters.length + values[v], 0);
}
function encode(value, characters, length = 0) {
let result = '';
do {
result = characters[value % characters.length] + result;
value = Math.floor(value / characters.length);
} while (value);
return result.padStart(length, characters[0]);
}
console.log(decode('AAA', 'ABCDEF')); // 0
console.log(encode(0, 'ABCDEF', 3)); // AAA
console.log(encode(1, 'ABCDEF', 3)); // AAB
console.log(decode('CBA', 'ABCDEF')); // 78
console.log(encode(78, 'ABCDEF', 3)); // CBA
console.log(encode(79, 'ABCDEF', 3)); // CBB
This approach rotates each character by a fixed amount (which could be improved upon), ignoring the other characters.
let ar1 = ["A", "B", "C", "D", "E"];
function scramble(str) {
let rotation=3;
let s = str.split('');
for (let i=0; i<s.length; i++) {
s[i]=ar1[(parseInt(s[i],16)+rotation) % ar1.length];
}
return s.toString().replace(/,/g, '');
}

How to remove only one of repeated chars in string using JavaScript

I have a string with repeated chars like : 'CANADA'.
And I am trying to get the string which removed only one of repeated chars :
'CNADA', 'CANDA', 'CANAD'.
I've tried it with subString, but it returned the part of string removed.
Also I've tried it with reduce, but it ended up removing all the repeated chars ('CND').
What is the way of removing only one char at time?
The results can be stored in array. (results = ['CNADA', 'CANDA', 'CANAD'])
Thank you.
You can achieve this by utilizing the second parameter of String#indexOf() which specifies the position from which to start the search. Here in a while loop, and using a Set to remove dulplicates before returning.
function getReplaceOptions(str, char) {
let res = [], i = str.indexOf(char, 0);
while (i !== -1) {
res.push(str.substring(0, i) + str.substring(++i));
i = str.indexOf(char, i)
}
return Array.from(new Set(res))
}
console.log(getReplaceOptions('CANADA', 'A'));
console.log(getReplaceOptions('Mississippi', 's'));
You can first count all the occurrences in the string. Later you can iterate over the script and if the count is greater than 1 you can remove that character.
const theString = 'CANADA'
const letterCount = {}
const resultArr = []
for (var i = 0; i < theString.length; i++) {
const theLetter = theString.charAt(i)
if(letterCount[theLetter]){
letterCount[theLetter] = letterCount[theLetter] + 1
}
else{
letterCount[theLetter] = 1
}
}
console.log(letterCount)
for (var i = 0; i < theString.length; i++) {
const theLetter = theString.charAt(i)
if(letterCount[theLetter] && letterCount[theLetter] > 1){
resultArr.push(theString.substr(0, i) + theString.substr(i + 1))
}
}
console.log(resultArr)
If you want to remove only the first repeating character then you can use matchAll here as:
Just see the browser compatibility before using this
const str = 'CANADA';
const firstRepeatedChar = 'A';
const result = [];
for (let { index } of str.matchAll(firstRepeatedChar)) {
result.push(str.slice(0, index) + str.slice(index + 1));
}
console.log(result);
NOTE: If you want to search for the first repeating character then remove it, then you can do as:
const str = 'CANADA';
let firstRepeatedChar = '';
const set = new Set();
for (let i = 0; i < str.length; ++i) {
if (!set.has(str[i])) {
set.add(str[i]);
} else {
firstRepeatedChar = str[i];
break;
}
}
const result = [];
for (let { index } of str.matchAll(firstRepeatedChar)) {
result.push(str.slice(0, index) + str.slice(index + 1));
}
console.log(result);
You could use some Array magic to remove duplicate characters:
function removeDuplicateCharacters(value) {
// convert string to array and loop through each character
return String(value).split('').filter(function(char, index, all) {
// return false if char found at a different index
return (index === all.indexOf(char));
})
.join(''); // convert back to a string
}
// returns `CAND`
removeDuplicateCharacters('CANADA');
// returns `helo wrd`
removeDuplicateCharacters('hello world');

Find indices within a string where any combination of an array of words is found

Sample data:
String: "barfoofoobarthefoobarman"
Array of words: ["bar", "foo", "the"]
Output:
[6, 9, 12]
I was asked this question during an interview. Due to time constraint, I tried to find all the possible words that could be made out of the array of words (i. e. "barfoothe"), but was told that would not scale for large arrays. Was suggested to use a map data structure, but I think my solution doesn't scale either, and it's brute forced.
Here's the solution.
var solution = function(string, words) {
let output = [];
let wordsMap = new Map();
let wordsNumber = words.length;
let wordLength = words[0].length;
words.forEach((word) => {
if (!wordsMap.has(word))
wordsMap.set(word, 1);
else
wordsMap.set(word, wordsMap.get(word) + 1);
});
for (let i = 0; i <= string.length-(wordsNumber*wordLength); i+=wordLength) {
let tempMap = new Map(wordsMap);
let check = true;
let tempString = string.substring(i, i + wordsNumber*wordLength);
for (let j = 0; j <= tempString.length - wordLength; j += wordLength) {
let tempString2 = tempString.substring(j, j + wordLength);
if (tempMap.has(tempString2))
tempMap.set(tempString2, tempMap.get(tempString2) - 1);
}
for (let val of tempMap.values()){
if (val !== 0){
check = false
break;
}
}
if (check)
output.push(i)
}
console.log(output);
}
solution("barfoothefoobarman", ["foo", "bar"]);
Any suggestion for a smarter solution?
You could create a dynamic regular expression.
const words = ['foo', 'bar']
const rx = new RegExp(words.join('|'), 'g')
// todo escape special characters
Then search away.
const counts = words.map(it=>0) // [0,0]
// todo use map or object to track counts instead of array
while (m = rx.exec(inputString)) {
const index = words.indexOf(m[0])
counts[index]++
}
Thank you for your question. I think the question in the interview was less about the right solution and more about the right approach.
The trickiest part is actually just finding the word combinations. There are several approaches here. For me it's a clear case for recursion.
So my approach would be:
find all word combinations, except combinations with itself (for example: foofoo or barbar).
iterate through the word combinations and ask whether they are contained in the string.
extra: Sort SolutionArray
Done!
Note: I use indexOf() for point 2 but I think a regex match would make it even better because you find all possibilities of a word in a string and not just the first one like with indexOf. Would make sense for longer strings.
const arr = ["foo", "bar"];
const str = "barfoothefoobarman"
let res = [];
const combinations = (len, val, existing) => {
if (len == 0) {
res.push(val);
return;
}
for(let i=0; i<arr.length; i++) {
if(! existing[i]) {
existing[i] = true;
combinations(len-1, val + arr[i], existing);
existing[i] = false;
}
}
}
const buildCombinations = (arr = []) => {
for(let i = 0; i < arr.length; i++) {
combinations(arr.length - i, "", []);
}
};
buildCombinations(arr);
// exclude the base wordes from result array
newRes = res.filter((e) => {
if (! arr.includes(e)) {
return e;
}
})
console.log('all word combinations:', newRes);
// get the string position
const _positions = [];
newRes.forEach((w) => {
let res = str.indexOf(w);
if (res != -1 && ! _positions.includes(res)) {
_positions.push(res);
}
})
// sort array and use Float64Array to speed up
const positions = new Float64Array(_positions)
console.log('positions', positions.sort())

Get groups between two strings

So I got a string:
let string = "XABXAX12345BX293993AX9393B33AXAXBXBXBXAAABBX";
and I'd like to extract all occurrences between the strings AX and BXs to get an array like this as result:
let result = ["12345", "9393B33AXAX"];
I've tried to use some kind of regex but I was not really successfull tbh.
let result = string.split(/AX([^AXBX]+)BX/);
Another aproach was a simple for-loop but also this is not working as I've expected. So maybe somebody is able to help me fixing the issues. Please have a look at my code:
let string = "XABXAX12345BX293993AX9393B33AXAXBXBXBXAAABBX"
let result = [];
for (let i=0; i<string.length; i++) {
if (string[i] == "A" && string[i+1] === "X") {
for (let j=i; j<string.length; j++) {
if (string[j] == "B" && string[j+1] === "X") {
let substring = string.substring(i+1, j+1);
result.push(substring)
break;
}
}
}
}
console.log(result);
Here's a simple solution:
function re_esc(str) {
return str.replace(/\W/g, "\\$&");
}
const start = "AX";
const end = "BX";
const re = new RegExp(re_esc(start) + '([\\s\\S]*?)' + re_esc(end), 'g');
const string = "XABXAX12345BX293993AX9393B33AXAXBXBXBXAAABBX";
const results = [];
let m;
while (m = re.exec(string)) {
results.push(m[1]);
}
console.log(results);
We build a regex of the form START(.*?)END, then use it to successively extract matches in a loop.
Here's a relatively straightforward looping approach that doesn't use regexes:
function findOccurrences(str, fromStr, toStr) {
const occurrences = [];
let startIndex = 0;
while (true) {
const fromIndex = str.indexOf(fromStr, startIndex);
if (fromIndex === -1) {
break;
}
const toIndex = str.indexOf(toStr, fromIndex + fromStr.length);
if (toIndex === -1) {
break;
}
const occurrence = str.slice(fromIndex + fromStr.length, toIndex);
occurrences.push(occurrence);
startIndex = toIndex + toStr.length;
}
return occurrences;
}
console.log(
findOccurrences("XABXAX12345BX293993AX9393B33AXAXBXBXBXAAABBX",
"AX", "BX"));
This doesn't include any sanity checks; for instance, you might want to check that fromStr and toStr aren't empty strings.

Alibaba interview: print a sentence with min spaces

I saw this interview question and gave a go. I got stuck. The interview question is:
Given a string
var s = "ilikealibaba";
and a dictionary
var d = ["i", "like", "ali", "liba", "baba", "alibaba"];
try to give the s with min space
The output may be
i like alibaba (2 spaces)
i like ali baba (3 spaces)
but pick no.1
I have some code, but got stuck in the printing.
If you have better way to do this question, let me know.
function isStartSub(part, s) {
var condi = s.startsWith(part);
return condi;
}
function getRestStr(part, s) {
var len = part.length;
var len1 = s.length;
var out = s.substring(len, len1);
return out;
}
function recPrint(arr) {
if(arr.length == 0) {
return '';
} else {
var str = arr.pop();
return str + recPrint(arr);
}
}
// NOTE: have trouble to print
// Or if you have better ways to do this interview question, please let me know
function myPrint(arr) {
return recPrint(arr);
}
function getMinArr(arr) {
var min = Number.MAX_SAFE_INTEGER;
var index = 0;
for(var i=0; i<arr.length; i++) {
var sub = arr[i];
if(sub.length < min) {
min = sub.length;
index = i;
} else {
}
}
return arr[index];
}
function rec(s, d, buf) {
// Base
if(s.length == 0) {
return;
} else {
}
for(var i=0; i<d.length; i++) {
var subBuf = [];
// baba
var part = d[i];
var condi = isStartSub(part, s);
if(condi) {
// rest string
var restStr = getRestStr(part, s);
rec(restStr, d, subBuf);
subBuf.unshift(part);
buf.unshift(subBuf);
} else {
}
} // end loop
}
function myfunc(s, d) {
var buf = [];
rec(s, d, buf);
console.log('-- test --');
console.dir(buf, {depth:null});
return myPrint(buf);
}
// Output will be
// 1. i like alibaba (with 2 spaces)
// 2. i like ali baba (with 3 spaces)
// we pick no.1, as it needs less spaces
var s = "ilikealibaba";
var d = ["i", "like", "ali", "liba", "baba", "alibaba"];
var out = myfunc(s, d);
console.log(out);
Basically, my output is, not sure how to print it....
[ [ 'i', [ 'like', [ 'alibaba' ], [ 'ali', [ 'baba' ] ] ] ] ]
This problem is best suited for a dynamic programming approach. The subproblem is, "what is the best way to create a prefix of s". Then, for a given prefix of s, we consider all words that match the end of the prefix, and choose the best one using the results from the earlier prefixes.
Here is an implementation:
var s = "ilikealibaba";
var arr = ["i", "like", "ali", "liba", "baba", "alibaba"];
var dp = []; // dp[i] is the optimal solution for s.substring(0, i)
dp.push("");
for (var i = 1; i <= s.length; i++) {
var best = null; // the best way so far for s.substring(0, i)
for (var j = 0; j < arr.length; j++) {
var word = arr[j];
// consider all words that appear at the end of the prefix
if (!s.substring(0, i).endsWith(word))
continue;
if (word.length == i) {
best = word; // using single word is optimal
break;
}
var prev = dp[i - word.length];
if (prev === null)
continue; // s.substring(i - word.length) can't be made at all
if (best === null || prev.length + word.length + 1 < best.length)
best = prev + " " + word;
}
dp.push(best);
}
console.log(dp[s.length]);
pkpnd's answer is along the right track. But word dictionaries tend to be quite large sets, and iterating over the entire dictionary at every character of the string is going to be inefficient. (Also, saving the entire sequence for each dp cell may consume a large amount of space.) Rather, we can frame the question, as we iterate over the string, as: given all the previous indexes of the string that had dictionary matches extending back (either to the start or to another match), which one is both a dictionary match when we include the current character, and has a smaller length in total. Generally:
f(i) = min(
f(j) + length(i - j) + (1 if j is after the start of the string)
)
for all j < i, where string[j] ended a dictionary match
and string[j+1..i] is in the dictionary
Since we only add another j when there is a match and a new match can only extend back to a previous match or to the start of the string, our data structure could be an array of tuples, (best index this match extends back to, total length up to here). We add another tuple if the current character can extend a dictionary match back to another record we already have. We can also optimize by exiting early from the backwards search once the matched substring would be greater than the longest word in the dictionary, and building the substring to compare against the dictionary as we iterate backwards.
JavaScript code:
function f(str, dict){
let m = [[-1, -1, -1]];
for (let i=0; i<str.length; i++){
let best = [null, null, Infinity];
let substr = '';
let _i = i;
for (let j=m.length-1; j>=0; j--){
let [idx, _j, _total] = m[j];
substr = str.substr(idx + 1, _i - idx) + substr;
_i = idx;
if (dict.has(substr)){
let total = _total + 1 + i - idx;
if (total < best[2])
best = [i, j, total];
}
}
if (best[0] !== null)
m.push(best);
}
return m;
}
var s = "ilikealibaba";
var d = new Set(["i", "like", "ali", "liba", "baba", "alibaba"]);
console.log(JSON.stringify(f(s,d)));
We can track back our result:
[[-1,-1,-1],[0,0,1],[4,1,6],[7,2,10],[11,2,14]]
[11, 2, 14] means a total length of 14,
where the previous index in m is 2 and the right index
of the substr is 11
=> follow it back to m[2] = [4, 1, 6]
this substr ended at index 4 (which means the
first was "alibaba"), and followed m[1]
=> [0, 0, 1], means this substr ended at index 1
so the previous one was "like"
And there you have it: "i like alibaba"
As you're asked to find a shortest answer probably Breadth-First Search would be a possible solution. Or you could look into A* Search.
Here is working example with A* (cause it's less bring to do than BFS :)), basically just copied from Wikipedia article. All the "turning string into a graph" magick happens in the getNeighbors function
https://jsfiddle.net/yLeps4v5/4/
var str = 'ilikealibaba'
var dictionary = ['i', 'like', 'ali', 'baba', 'alibaba']
var START = -1
var FINISH = str.length - 1
// Returns all the positions in the string that we can "jump" to from position i
function getNeighbors(i) {
const matchingWords = dictionary.filter(word => str.slice(i + 1, i + 1 + word.length) == word)
return matchingWords.map(word => i + word.length)
}
function aStar(start, goal) {
// The set of nodes already evaluated
const closedSet = {};
// The set of currently discovered nodes that are not evaluated yet.
// Initially, only the start node is known.
const openSet = [start];
// For each node, which node it can most efficiently be reached from.
// If a node can be reached from many nodes, cameFrom will eventually contain the
// most efficient previous step.
var cameFrom = {};
// For each node, the cost of getting from the start node to that node.
const gScore = dictionary.reduce((acc, word) => { acc[word] = Infinity; return acc }, {})
// The cost of going from start to start is zero.
gScore[start] = 0
while (openSet.length > 0) {
var current = openSet.shift()
if (current == goal) {
return reconstruct_path(cameFrom, current)
}
closedSet[current] = true;
getNeighbors(current).forEach(neighbor => {
if (closedSet[neighbor]) {
return // Ignore the neighbor which is already evaluated.
}
if (openSet.indexOf(neighbor) == -1) { // Discover a new node
openSet.push(neighbor)
}
// The distance from start to a neighbor
var tentative_gScore = gScore[current] + 1
if (tentative_gScore >= gScore[neighbor]) {
return // This is not a better path.
}
// This path is the best until now. Record it!
cameFrom[neighbor] = current
gScore[neighbor] = tentative_gScore
})
}
throw new Error('path not found')
}
function reconstruct_path(cameFrom, current) {
var answer = [];
while (cameFrom[current] || cameFrom[current] == 0) {
answer.push(str.slice(cameFrom[current] + 1, current + 1))
current = cameFrom[current];
}
return answer.reverse()
}
console.log(aStar(START, FINISH));
You could collect all possible combinations of the string by checking the starting string and render then the result.
If more than one result has the minimum length, all results are taken.
It might not work for extrema with string who just contains the same base string, like 'abcabc' and 'abc'. In this case I suggest to use the shortest string and update any part result by iterating for finding longer strings and replace if possible.
function getWords(string, array = []) {
words
.filter(w => string.startsWith(w))
.forEach(s => {
var rest = string.slice(s.length),
temp = array.concat(s);
if (rest) {
getWords(rest, temp);
} else {
result.push(temp);
}
});
}
var string = "ilikealibaba",
words = ["i", "like", "ali", "liba", "baba", "alibaba"],
result = [];
getWords(string);
console.log('all possible combinations:', result);
console.log('result:', result.reduce((r, a) => {
if (!r || r[0].length > a.length) {
return [a];
}
if (r[0].length === a.length) {
r.push(a);
}
return r;
}, undefined))
Use trie data structure
Construct a trie data structure based on the dictionary data
Search the sentence for all possible slices and build a solution tree
Deep traverse the solution tree and sort the final combinations
const sentence = 'ilikealibaba';
const words = ['i', 'like', 'ali', 'liba', 'baba', 'alibaba',];
class TrieNode {
constructor() { }
set(a) {
this[a] = this[a] || new TrieNode();
return this[a];
}
search(word, marks, depth = 1) {
word = Array.isArray(word) ? word : word.split('');
const a = word.shift();
if (this[a]) {
if (this[a]._) {
marks.push(depth);
}
this[a].search(word, marks, depth + 1);
} else {
return 0;
}
}
}
TrieNode.createTree = words => {
const root = new TrieNode();
words.forEach(word => {
let currentNode = root;
for (let i = 0; i < word.length; i++) {
currentNode = currentNode.set(word[i]);
}
currentNode.set('_');
});
return root;
};
const t = TrieNode.createTree(words);
function searchSentence(sentence) {
const marks = [];
t.search(sentence, marks);
const ret = {};
marks.map(mark => {
ret[mark] = searchSentence(sentence.slice(mark));
});
return ret;
}
const solutionTree = searchSentence(sentence);
function deepTraverse(tree, sentence, targetLen = sentence.length) {
const stack = [];
const sum = () => stack.reduce((acc, mark) => acc + mark, 0);
const ret = [];
(function traverse(tree) {
const keys = Object.keys(tree);
keys.forEach(key => {
stack.push(+key);
if (sum() === targetLen) {
const result = [];
let tempStr = sentence;
stack.forEach(mark => {
result.push(tempStr.slice(0, mark));
tempStr = tempStr.slice(mark);
});
ret.push(result);
}
if(tree[key]) {
traverse(tree[key]);
}
stack.pop();
});
})(tree);
return ret;
}
const solutions = deepTraverse(solutionTree, sentence);
solutions.sort((s1, s2) => s1.length - s2.length).forEach((s, i) => {
console.log(`${i + 1}. ${s.join(' ')} (${s.length - 1} spaces)`);
});
console.log('pick no.1');

Categories

Resources