How to remove only one of repeated chars in string using JavaScript - 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');

Related

Longest prefix that match more than 50% of items in an array

Suppose I have array of strings: ["apple", "ape", "application", "item"].
I need to find the longest common prefix that matches more than 50% of the strings in that array.
For example, we got "ap" being the prefix of 3 strings in a 4 elements array -> so more than 50% of the array -> returns the prefix.
This is my attempt:
const longestPrefix = (arrStr) => {
if (arrStr.length === 0) return "";
let prefix = "";
let noPrefixMatched = {};
// loop the characters of first word
for (let i = 0; i < arrStr[0].length; i++) {
let char = arrStr[0][i];
let j = 0;
// loop all the words of the array except first one
for (j = 1; j < arrStr.length; j++) {
if (arrStr[j][i] !== char) {
// if first char not matched then set that word as notMatched
if (i === 0) {
noPrefixMatched[arrStr[j]] = true;
}
break;
}
// if the first characters are equal in all words and the loop reach the final word
if (arrStr[j][i] === char && j === arrStr.length - 1) {
prefix += char;
}
}
}
return prefix;
};
I try to get the most common prefix by vertical comparison, but it's not working with a word without any prefix in the array (like "item" in the above array). How should I do this?
One way to do this is to iterate all the words, constructing prefixes one letter at a time and counting the occurrence of each prefix as you see it. You can then filter that result based on the count being greater than 1/2 the length of the input array, and finally sort it by the prefix length descending, returning the first entry in the result:
const words = ["apple", "ape", "application", "item"]
const counts = words.reduce((acc, w) => {
prefix = ''
for (i = 0; i < w.length; i++) {
prefix += w[i]
acc[prefix] = (acc[prefix] || 0) + 1
}
return acc
}, {})
const bestPrefix = Object.entries(counts)
.filter(([_, v]) => v > words.length / 2.0)
.sort((a, b) => b[0].length - a[0].length)
[0][0]
console.log(bestPrefix)
The first word should not be considered special or be hardcoded in any way - it may not even be a match for the substring selected, after all.
A simple way to code this would be to iterate over all words and their possible prefixes and see which prefixes pass the test, while keeping the best one in an outer variable - then return it at the end.
const getPrefixes = str => Array.from(str, (_, i) => str.slice(0, i + 1));
const matches = (arr, prefix) => {
const matchCount = arr.reduce((a, str) => a + str.startsWith(prefix), 0);
return matchCount / arr.length > 0.5;
};
const longestPrefix = (arrStr) => {
let bestPrefix = '';
for (const str of arrStr) {
for (const prefix of getPrefixes(str)) {
if (prefix.length > bestPrefix.length && matches(arrStr, prefix)) {
bestPrefix = prefix;
}
}
}
return bestPrefix;
};
console.log(longestPrefix(["apple", "ape", "application", "item"]));
A less computationally complex but more complicated method would be to construct a tree structure of characters from each string in the input, and then iterate through the tree to identify which nodes have enough nested children, and then pick the longest such node. This has the advantage of only requiring iterating over each character of each string in the input once.
const getBestChild = (obj, totalRequired, prefix = '') => {
if (obj.count < totalRequired) return;
const thisResult = { count: obj.count, prefix };
if (!obj.children) {
return thisResult;
}
let bestChild = thisResult;
for (const [nextChar, child] of Object.entries(obj.children)) {
const result = getBestChild(child, totalRequired, prefix + nextChar);
if (result && result.prefix.length > bestChild.prefix.length) {
bestChild = result;
}
}
return bestChild;
};
const longestPrefix = (arrStr) => {
const root = {};
for (const str of arrStr) {
let obj = root;
for (const char of str) {
if (!obj.children) {
obj.children = {};
}
const { children } = obj;
if (!children[char]) {
children[char] = { count: 0 };
}
children[char].count++;
obj = children[char];
}
}
const { length } = arrStr;
const totalRequired = length % 2 === 0 ? (1 + length / 2) : Math.ceil(length / 2);
return getBestChild(root, totalRequired).prefix;
};
console.log(longestPrefix(["apple", "ape", "application", "item"]));

Split string to equal length substrings in javascript

I have a string, for example "8FPHFW08" and I want to get these substrings: "8F000000", "00PH0000","0000FW00" , "00000008".
The relative python fuction is this:
def split_str(s):
res = []
for i in range(0,len(s),2):
a = ['0']*len(s)
a[i:i+2] = s[i:i+2]
res.append("".join(a))
return res
This is my attempt but I need 0 in empty positions
function split_olc(olc) {
var splitted = []
splitted.push(olc.match(/(..?)/g))
console.log(splitted[0])
return splitted[0]
}
How can I do the same thing in Javascript?
JavaScript strings are immutable, so there's no fancy shortcut for "overwrite a substring with another substring". You have to slice it up yourself.
Start with a "template", a string of the appropriate length with all zeroes, then splice it and your subject string appropriately.
const template = s.replace(/./g,'0');
const res = [];
for( let i=0; i<s.length; i+=2) {
res.push(
template.substring(0, i)
+ s.substring(i, i+2)
+ template.substring(i+2)
);
}
return res;
Not sure this is the best way to learn a new language, but I've tried to give you the best one-for-one translation of python to js of your code:
function split_str(s) { // def split_str(s):
const res = [] // res = []
for (let i = 0; i < s.length; i += 2) { // for i in range(0,len(s),2):
const a = new Array(s.length).fill('0'); // a = ['0']*len(s)
a.splice(i, 2, s[i], s[i+1]); // a[i:i+2] = s[i:i+2]
res.push(a.join('')); // res.append("".join(a))
}
return res; // return res
}
console.log(split_str('helloworld'))
Use slice to get the partial string. Use padStart and padEnd fill the start and end with 0
function replace(str) {
const len = str.length,
output = []
for (let i = 0; i < len; i += 2) {
output.push(
str.slice(i, i+2)
.padStart(i+2, '0')
.padEnd(len, '0')
)
}
return output
}
console.log(
...replace("8FPHFW08")
)

Captalize letter in a string in order and create an array to store

// Create a function that takes a string and turns it into a Mexican Wave.
var smallarr=[]
var bigarr=[]
var another=""
function wave(str){
for (var i=0;i<str.length;i++){
smallarr.push(str)
}
for (var j=0;j<smallarr.length;j++)
{
if(smallarr[j][j].toUpperCase()==smallarr[j][j])
{
var c=smallarr[j][j].toLowerCase()
smallarr[j][j]=c
}
else {
var c=smallarr[j][j].toUpperCase()
smallarr[j][j]=c}
}
}
return smallarr
}
document.write(wave("edabit"))
//console.log(smallarr)
The output I expect is wave("edabit") ➞ ["Edabit", "eDabit", "edAbit", "edaBit", "edabIt", "edabiT"] but now the output is the same as the input.
Any advice is appreciated.
You can split the input string into an array of characters and then map over it replacing the current character with it's uppercase version:
const wave = (str) => {
return str
.split('')
.map((char, index) =>
str.substr(0, index) + char.toUpperCase() + str.substr(index + 1))
}
console.log(wave('edabit'));
You can use Array.from() to create an array from the string. In the callback (mapFn) get the current letter, and the current index, use them with String.substring() to build a new string with the uppercased letter.
const fn = str => Array.from(str, (c, i) =>
str.substring(0, i) + c.toUpperCase() + str.substring(i + 1)
);
const result = fn('edabit');
console.log(result);
You can try double Array.map() where second callback argument represents an index:
let input = "edabit";
let result = input.split('').map(
(_, i) => input.split('').map(
(char, j) => i === j ? char.toUpperCase() : char).join()
);
console.log(result);
EDIT:
The problem with your approach is that strings are immutable so you need to build a new string using slice like below:
var smallarr=[]
var bigarr=[]
var another=""
function wave(str){
for (var i=0;i<str.length;i++){
smallarr.push(str)
}
for (var j=0;j<smallarr.length;j++){
smallarr[j] = smallarr[j].slice(0,j) + smallarr[j][j].toUpperCase() + smallarr[j].slice(j+1);
}
return smallarr
}
document.write(wave("edabit"))
console.log(smallarr)
or just using one loop:
function wave(str){
var smallarr=[]
for (var i=0;i<str.length;i++){
smallarr.push(str.slice(0,i) + str[i].toUpperCase() + str.slice(i+1))
}
return smallarr
}
console.log(wave("edabit"))
The main issue with your code is that you try to update a character in a string, but that is not possible. In JavaScript strings are immutable. Trying to assign to a certain index will have no effect to the string.
Instead you must rebuild a string where that one character is different. You can use slice to take parts of the string to help you build it.
Here is your code adapted to that effect:
function wave(str) {
var smallarr = []; // define as local
for (var j = 0; j < str.length; j++) {
let c = str[j]; // Use a variable to avoid duplicate code
if (c.toUpperCase() === c) {
c = c.toLowerCase();
} else {
c = c.toUpperCase();
}
// You cannot modify a string; you need to regenerate one
smallarr.push(str.slice(0, j) + c + str.slice(j+1));
}
return smallarr
}
console.log(wave("edabit"))
String.prototype.replaceAt=function(index, char) {
var a = this.split("");
a[index] = char;
return a.join("");
}
const createEdibet = ( word ) => {
console.log('word ' + word.split(''));
let array = word.split('')
array.map((letter, i) => {
let a = word.replaceAt(i, letter.toUpperCase());
console.log(a);
return a;
});
console.log(array)
// console.log(words)
}
createEdibet('edabit');
You could split the string, map the array and take the splittes string for another mapping by checking the index and use an upper case letter for matching indices.
const
wave = (string) => string
.split('')
.map((_, i, splitted) => splitted.map((c, j) => i === j ? c.toUpperCase() : c).join(''));
};
console.log(wave('edabit'));
A classic approach
function wave(string) {
var result = [],
i, j, temp;
for (i = 0; i < string.length; i++) {
temp = '';
for (j = 0; j < string.length; j++) {
temp += i === j ? string[j].toUpperCase() : string[j];
}
result.push(temp);
}
return result;
}
console.log(wave('edabit'));
If the array length is small you can use split, map, join like this:
let text = `edabit`;
let results = text.split('').map((char,index,original)=>{
let temp = [...original];
temp[index]=temp[index].toUpperCase();
return temp.join('');
});
console.log(results);
But in big array it's not optimized.

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.

Cut JS string present in an array at every occurrance of a particular substring and append to same array

Note:
At this point in time, I'm unable to word the question title better. If someone is able to put it accross better, please go right ahead!
What I have:
var array = ["authentication.$.order", "difference.$.user.$.otherinformation", ... , ...]
What I need:
["authentication", "authentication.$", "authentication.$.order",
"difference", "difference.$", "difference.$.user", "difference.$.user.$",
"difference.$.user.$.otherinformation"]
Basically, wherevever I see .$., I need to preserve it, then append everything before the occourrence of .$. along with everything before the occourrence of .$
Example:
difference.$.user.$.otherinformation should be parsed to contain:
difference
difference.$
difference.$.user
difference.$.user.$
difference.$.user.$.otherinformation
I'm strongly feeling that some sort of recursion is to be involved here, but have not progressed in that direction yet.
Below is my implementation for the same, but unfortunately, my when my substring matches the first occourrence of .$., it stops and does not proceed to further check for other occurrences of .$. in the same string.
How best can I take this to closure?
Current flawed implementation:
for(var i=0; i<array.length; i++){
// next, replace all array field references with $ as that is what autoform's pick() requires
// /\.\d+\./g,".$." ==> replace globally .[number]. with .$.
array[i] = array[i].replace(/\.\d+\./g,".$.");
if(array[i].substring(0, array[i].lastIndexOf('.$.'))){
console.log("Substring without .$. " + array[i].substring(0, array[i].indexOf('.$.')));
console.log("Substring with .$ " + array[i].substring(0, array[i].indexOf('.$.')).concat(".$"));
array.push(array[i].substring(0, array[i].indexOf('.$.')).concat(".$"));
array.push(array[i].substring(0, array[i].indexOf('.$.')));
}
}
// finally remove any duplicates if any
array = _.uniq(array);
A functional single liner could be;
var array = ["authentication.$.order", "difference.$.user.$.otherinformation"],
result = array.reduce((r,s) => r.concat(s.split(".").reduce((p,c,i) => p.concat(i ? p[p.length-1] + "." + c : c), [])), []);
console.log(result);
You can use this function inside your array loop.
var test = "difference.$.user.$.otherinformation";
function toArray(testString) {
var testArr = testString.split(".")
var tempString = "";
var finalArray = [];
for (var i = 0; i < testArr.length; i++) {
var toTest = testArr[i];
if (toTest == "$") {
tempString += ".$"
} else {
if (i != 0) {
tempString += ".";
}
tempString += toTest;
}
finalArray.push(tempString)
}
return finalArray;
}
console.log(toArray(test))
I used a Regex expression to grab everything until the last occurrence of .$ and the chopped it, until there was nothing left. Reverse at the end.
let results = [];
let found = true;
const regex = /^(.*)\.\$/g;
let str = `difference.\$.user.\$.otherinformation`;
let m;
results.push(str);
while(found) {
found = false;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(m.length > 0) {
found = true;
results.push(m[0]);
str = m[1];
}
}
}
results.push(str);
results = results.reverse();
// Concat this onto another array and keep concatenating for the other strings
console.log(results);
You will just need to loop this over your array, store the results in a temp array and keep concatenating them onto a final array.
https://jsfiddle.net/9pa3hr46/
You can use reduce as follows:
const dat = ["authentication.$.order", "difference.$.user.$.otherinformation"];
const ret = dat.reduce((acc, val) => {
const props = val.split('.');
let concat = '';
return acc.concat(props.reduce((acc1, prop) => {
concat+= (concat ? '.'+ prop : prop);
acc1.push(concat);
return acc1;
}, []));
}, [])
console.log(ret);
Actually recursion is unnecessary for this problem. You can use regular loop with subloop instead.
All you need is:
split each occurence in the array into substrings;
build a series of accumulated values from these substrings;
replace the current element of the array with this series.
Moreover, in order to make replacement to work properly you have to iterate the array in reverse order. BTW in this case you don't need to remove duplicates in the array.
So the code should look like this:
var array = ["authentication.$.order", "difference.$.user.$.otherinformation"];
var SEP = '.$.';
for (var i = array.length-1; i >= 0; i--){
var v = array[i];
var subs = v.replace(/\.\d+\./g, SEP).split(SEP)
if (subs.length <= 1) continue;
var acc = subs[0], elems = [acc];
for (var n = subs.length-1, j = 0; j < n; j++) {
elems[j * 2 + 1] = (acc += SEP);
elems[j * 2 + 2] = (acc += subs[j]);
}
array.splice.apply(array, [i, 1].concat(elems));
}
console.log(array);
Use a simple for loop like below:
var str = "difference.$.user.$.otherinformation";
var sub, initial = "";
var start = 0;
var pos = str.indexOf('.');
for (; pos != -1; pos = str.indexOf('.', pos + 1)) {
sub = str.substring(start, pos);
console.log(initial + sub);
initial += sub;
start = pos;
}
console.log(str);

Categories

Resources