Function to count duplicate characters in a string - javascript

I am trying to write a function which finds all unique characters in a provided string.
I'd like the function to return the results in the following format:
removeDuplicates('th#elex_ash?') -> {unique: 'aehlstx', duplicates: 2}
So far I have come up with the following attempted solution:
function removeDuplicates(str){
var unique ="";
for (var i=0; i<str.length; i++){
if(unique.indexOf(str[i]) == -1){
unique += str[i];
}
}
unique = unique.replace(/[&\/\\#,_+()$~%.'":*?<>]/g, '');
return unique.split('').sort().join('');
}
console.log(removeDuplicates('aaabbbac'));
console.log(removeDuplicates('a'));
console.log(removeDuplicates('th#elex_ash?'));

If you are writing for an environment where you can use newer javascript features. Set makes this a little easier since it will enforce uniqueness among the contents. You can also compare the lengths of the cleaned string with the uniques to find the duplicate count
For example:
function removeDuplicates(str){
str = str.replace(/[&\/\\#,_+()$~%.'":*?<>]/g, '')
let unique = Array.from(new Set(str))
.sort((a, b) => a.localeCompare(b))
.join('')
let duplicates = str.length - unique.length
return {unique, duplicates}
}
console.log(removeDuplicates("th#elex_ash?"))

function removeDuplicates(str) {
var returnObject = {
unique : "",
duplicates : 0
};
for (var i = 0; i < str.length; i++){
if (returnObject.unique.indexOf(str[i]) < 0) {
returnObject.unique += str[i];
} else {
returnObject.duplicates++;
}
}
returnObject.unique = returnObject.unique.replace(/[&\/\\#,_+()$~%.'":*?<>]/g, '');
returnObject.unique = returnObject.unique.split('').sort().join('');
return returnObject;
}
console.log(removeDuplicates('aaabbbac'));
console.log(removeDuplicates('a'));
console.log(removeDuplicates('th#elex_ash?'));

You are very close!
An object can be returned in Javascript just like anything else; for instance, to return the format you were asking for, simply do the following:
return {
unique: unique,
duplicates: duplicates
}
Notice how your variable names are the same as your property keys? You can use a shorthand notation by simply writing unique and duplicates, rather than unique: unique and duplicates: duplicates.
You're not keeping a count of duplicates. To do so, simply add an else block to your if(unique.indexOf(str[i]) == -1) conditional, which should only be reached when a character is indeed a duplicate, and increment a counter - lets name it duplicates.
Complete Solution
function removeDuplicates(str) {
var unique = "";
var duplicates = 0;
for (var i = 0; i < str.length; i++) {
if (unique.indexOf(str[i]) == -1) {
unique += str[i];
} else {
duplicates++;
}
}
unique = unique.replace(/[&\/\\#,_+()$~%.'":*?<>]/g, '').split('').sort().join('')
return {
unique,
duplicates
}
}
const testString = 'th#elex_ash?';
const expectedOutput = {
unique: 'aehlstx',
duplicates: 2
};
console.log(removeDuplicates(testString));

Related

Word Break algorithm

I'm trying to implement the "Word Break" algorithm.
Problem:
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
My solution:
var wordBreak = function(s, wordDict) {
if(!wordDict || wordDict.length === 0)
return false;
while(wordDict.length > 0 || s.length > 0) {
const word = wordDict.shift();
const index = s.indexOf(word);
if(index === -1) {
return false;
}
s = s.substring(0, index) + s.substring(index+word.length, s.length);
}
return s.length === 0 && wordDict.length === 0 ? true : false;
};
It works for the example (input) above. However it fails for the input below.
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
How can I keep track of words that I already eliminate and check it at the end. This input above, the remaining s string contains "apple" which is in the word dictionary, so the output should be true.
Thanks
A simple Javascript solution.
This loops through the wordDict array and checks if each word exist in the str. If it doesn't that is when the indexOf the word return -1, the function returns false. However, if the words in the wordDict array are in the string, it returns true at the end of the for loop.
const wordBreak =(str, wordDict)=>{
if (!wordDict || wordDict.length === 0) return false
for(let i=0; I<wordDict.length; i++){
const dictIndex = str.indexOf(wordDict[i])
if(dictIndex === -1){
return false
}
}
return true
}
This is an interesting problem I met two years ago in a different context, i.e., query tokenization. In my case, the number of words in the dictionary was in the order of several million, therefore a recursive approach looking each time for a different word of the dictionary was not practicable. Furthermore, I needed to apply dynamic programming to solve the task for strict efficiency reasons.
First of all, I suggest you to use the AhoCorasick algorithm to find the words within your search string. The algorithm looks for an arbitrary number of patterns in a string in linear time in the length of the string regardless of the number of patterns to find (no more number of words times length of the string operation, indeed each find of a word in a string needs to scan the entire string..).
Luckily, I found a javascript implementation of the algorithm here.
Using the code linked above and dynamic programming to track the words appearing in your string, I wrote the following javascript solution:
function wordBreak(s, wordDict) {
const len = s.length;
const memoization_array_words = new Array(len).fill(null);
const memoization_array_scores = new Array(len).fill(0);
const wordScores = {};
wordDict.forEach(function(word) {
wordScores[word] = 1
});
automata = new AhoCorasick(wordDict);
results = automata.search(s);
results.forEach(function(result) {
// result[0] contains the end position
// result[1] contains the list of words ending in that position
const end_pos = result[0];
result[1].forEach(function(word) {
const prev_end_pos = end_pos - word.length;
const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
const score = prev_score + wordScores[word];
if (score > memoization_array_scores[end_pos]) {
memoization_array_words[end_pos] = word;
memoization_array_scores[end_pos] = score;
}
});
});
if (memoization_array_words[len-1] == null) {
return false;
}
solution = []
var pos_to_keep = len - 1;
while (pos_to_keep >= 0) {
const word = memoization_array_words[pos_to_keep];
solution.push(word);
pos_to_keep -= word.length;
}
return solution.reverse()
}
where memoization_array_words and memoization_array_scores are filled left to right when we meet a word occurring after a previous one or at the beginning of the string s. The code should be autoesplicative, but if you need any explanation write me a comment, please.
As a plus, I associated a score to each word (here is 1 for simplicity) that allows you to distinguish between the different solutions. For instance, if you associate to each word an importance score, you will end up with the tokenization with the greatest score. In the code above, the tokenization with the highest number of words.
Extended version: I testing over the wordDict with some if there is one of the worde that beginns at the test-string (indexOf==0). If so I shorten the string about the length of the word and call the function recursivly with the shortened string. Otherwise the string is not splitable and I return false. I go this way on till an error occurs or the length of the string is 0 and I win because everything goes allright.
Remark: The error when the WordBreak is not clearly like with s= "cars" wordDict = ["car","ca","rs"] is now fixed. For this I calling in the some-methode the algorithm recursivly. So if one way stops before ending I go backwards and search for alternatives till I found one or there is no possibility left.
Remarks to; array.some
In an array.forEach there can't used a break without using some ugly tricks (like try...catch and throwing an error), so I could use the classic variant of the for-loop. But there exists the array.some method this loops like a forEach-loop but there had only one of the elements to be return true so the result is true.
Example:
const array = [1, 2, 3, 4, 5];
// checks whether an element is even
const even = (element) => element % 2 === 0;
console.log(array.some(even));
Here is the code of the working algorithm.
var wordBreak = function(s, wordDict) {
if (!wordDict || wordDict.length === 0) return false;
while (s.length > 0) {
let test = wordDict.some( (word,index) => {
if (s.indexOf(word)===0) {
s_new = s.substr(word.length);
return wordBreak(s_new, wordDict);
}
});
if (!test ) return false;
s=s_new;
}
if (s.length === 0) return true;
}
s = "leetcode"; wordDict = ["leet", "code"];
console.log(wordBreak(s, wordDict));
s = "applepenapple"; wordDict = ["apple", "pen"];
console.log(wordBreak(s, wordDict));
s= "cars"; wordDict = ["car","ca","rs"];
console.log(wordBreak(s, wordDict));
function wordBreak(dict, str){
if (!str){
return true;
}
for (const word of dict){
if (str.startsWith(word)){
return wordBreak(dict, str.substring(word.length, str.length))
}
}
return false;
}
You could also probably optimize the loop over dict by pre-sorting the array and using binary search, but hopefully this gets the point across.
If you'd be looking for a Dynamic Programming solution, we'd use an array for recording, and then we'd loop through and keep track of the word.
This'll pass through in JavaScript:
const wordBreak = function(s, wordDict) {
const len = s.length
const dp = new Array(len + 1).fill(false)
dp[0] = true
for (let i = 1; i < len + 1; i++) {
for (let j = 0; j < i; j++) {
if (dp[j] === true && wordDict.includes(s.slice(j, i))) {
dp[i] = true
break
}
}
}
return dp[s.length]
}
In Python, we would have used a list (which is similar to an array of JavaScript) with the same size as our string:
class Solution:
def wordBreak(self, s, words):
dp = [False] * len(s)
for i in range(len(s)):
for word in words:
k = i - len(word)
if word == s[k + 1:i + 1] and (dp[k] or k == -1):
dp[i] = True
return dp[-1]
Similarly in Java, we'd have used a boolean[]:
public final class Solution {
public static final boolean wordBreak(
String s,
List<String> words
) {
if (s == null || s.length() == 0) {
return false;
}
final int len = s.length();
boolean[] dp = new boolean[len];
for (int i = 0; i < len; i++) {
for (int j = 0; j <= i; j++) {
final String sub = s.substring(j, i + 1);
if (words.contains(sub) && (j == 0 || dp[j - 1])) {
dp[i] = true;
break;
}
}
}
return dp[len - 1];
}
}
Here is LeetCode's DP solution:
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet=new HashSet(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
References
For additional details, please see the Discussion Board which you can find plenty of well-explained accepted solutions in there, with a variety of languages including efficient algorithms and asymptotic time/space complexity analysis1, 2.

Create new array by matching values passed in url using JavaScript

I am building a web application that is primarily using Tableau elements (filters, visualizations, etc.) and the problem I have run into is in passing a large amount (dynamic) of filtered parameters across web pages. I've been trying to work with the JavaScript below, but not having any luck.
function getFilterd()
{
var worksheets = vizFilter.getWorkbook().getActiveSheet().getWorksheets();
var prjArray = window.location.search.substring(1).split(',');
var newArray = [];
//Run through field/filter names
var displayFilters = function (filters) {
for (var i = 0; i < filters.length; i++) {
filterName = filters[i].getFieldName();
//If field/filter name == PRJECT then store selected values in an array
if (filterName == 'PRJECT') {
filterList = []
for (var z = 0; z < filters[i].getAppliedValues().length; z++) {
filterList[z] = filters[i].getAppliedValues()[z].value;
}
//Compare filterList values to prjArray and push to newArray if a match
for (var t = 0; t < filterList.length; t++) {
if (filterList[t].getAppliedValues()[t].value.substring(4) == prjArray) {
newArray.push(filterList[t]);
}
}
}
}
}
}
//Runs through each worksheet in active workbook (optional)
for (var worksheetIndex = 0; worksheetIndex < worksheets.length; worksheetIndex++){
worksheets[worksheetIndex].getFiltersAsync().then(displayFilters);
}
}
So I was finally able to figure this out. The logic below was incorrect as mentioned by Justin in his answer:
//Compare filterList values to prjArray and push to newArray if a match
for (var t = 0; t < filterList.length; t++) {
if (filterList[t].getAppliedValues()[t].value.substring(4) == prjArray) {
newArray.push(filterList[t]);
}
}
In addition, were some syntax errors in the if statement. The following is the revised statement that did return the desired array:
//Compare filterList values to prjArray and push to newArray if a match
newArray = []
for (var t = 0; t < filterList.length; t++){
if (prjArray.indexOf(filterList[t].substring(4)) != -1) {
newArray.push(filterList[t]);
};
}
When you do a split() on window.location.search.substring(1), you return an array. Here, you are comparing a substring against an array, and that is always going to return false.
if (filterList[t].getAppliedValues()[t].value.substring(4) == prjArray) {
newArray.push(filterList[t]);
}
Additionally, you are only putting values into filterList and trying to access them with getAppliedValues is not going to work.
You need to test if the substring is within the array. You can do this using includes() to determine if the array includes the value provided.
if (prjArray.includes(filterList[t].substring(4))) {
newArray.push(filterList[t]);
}
The includes() method is not completely supported by all browsers. If you need backward compatibility,you can do this using indexOf and test is it returns other than -1
if (prjArray.indexOf(filterList[t].substring(4)) !== -1) {
newArray.push(filterList[t]);
}

Using search method from string

I'm trying to count the number of times certain words appear in the strings. Every time I run it I get a
uncaught TypeErro: undefined is not a function
I just actually need to count the number of times each "major" appears.
Below is my code:
for(var i = 0; i < sortedarray.length; i++)
{
if(sortedarray.search("Multimedia") === true)
{
multimedia += 1;
}
}
console.log(multimedia);
Here is my csv file which is stored in a 1d array.
"NAME","MAJOR","CLASS STANDING","ENROLLMENT STATUS"
"Smith, John A","Computer Science","Senior","E"
"Johnson, Brenda B","Computer Science","Senior","E"
"Green, Daisy L","Information Technology","Senior","E"
"Wilson, Don A","Information Technology","Junior","W"
"Brown, Jack J","Multimedia","Senior","E"
"Schultz, Doug A","Network Administration","Junior","E"
"Webber, Justin","Business Administration","Senior","E"
"Alexander, Debbie B","Multimedia","Senior","E"
"St. John, Susan G","Information Technology","Junior","D"
"Finklestein, Harold W","Multimedia","Freshman","E"
You need to search inside each string not the array. To only search inside the "Major" column, you can start your loop at index 1 and increment by 4 :
var multimedia = 0;
for(var i = 1; i < sortedarray.length; i += 4)
{
if(sortedarray[i].indexOf("Multimedia") > -1)
{
multimedia += 1;
}
}
console.log(multimedia);
What you're probably trying to do is:
for(var i = 0; i < sortedarray.length; i++)
{
if(sortedarray[i].indexOf("Multimedia") !== -1)
{
multimedia++;
}
}
console.log(multimedia);
I use indexOf since search is a bit of overkill if you're not using regexes.
Also, I replaced the += 1 with ++. It's practically the same.
Here's a more straightforward solution. First you count all the words using reduce, then you can access them with dot notation (or bracket notation if you have a string or dynamic value):
var words = ["NAME","MAJOR","CLASS STANDING","ENROLLMENT STATUS"...]
var count = function(xs) {
return xs.reduce(function(acc, x) {
// If a word already appeared, increment count by one
// otherwise initialize count to one
acc[x] = ++acc[x] || 1
return acc
},{}) // an object to accumulate the results
}
var counted = count(words)
// dot notation
counted.Multimedia //=> 3
// bracket notation
counted['Information Technology'] //=> 3
I don't know exactly that you need this or not. But I think its better to count each word occurrences in single loop like this:
var occurencesOfWords = {};
for(var i = 0; i < sortedarray.length; i++)
{
var noOfOccurences = (occurencesOfWords[sortedarray[i]]==undefined?
1 : ++occurencesOfWords[sortedarray[i]]);
occurencesOfWords[sortedarray[i]] = noOfOccurences;
}
console.log(JSON.stringify(occurencesOfWords));
So you'll get something like this in the end:
{"Multimedia":3,"XYZ":2}
.search is undefined and isn't a function on the array.
But exists on the current string you want to check ! Just select the current string in the array with sortedarray[i].
Fix your code like that:
for(var i = 0; i < sortedarray.length; i++)
{
if(sortedarray[i].search("Multimedia") === true)
{
multimedia += 1;
}
}
console.log(multimedia);

A function that will search an array for letters and return the position in which those letters are

I've created a function that will search for individual letters from a string regardless of case and order. Here is what it looks like.
function match(string, pattern) {
string = string.toLowerCase();
pattern = pattern.toLowerCase();
var patternarray = pattern.split("");
for (i = 0; i < pattern.length; i++) {
if (patternarray[i] >= "a" && patternarray[i] <= "z") {
if (string.indexOf(patternarray[i]) == -1) return false
}
}
return true
}
Now I want to do a similar thing except this time I will be searching an array, and instead of returning true/false I would like to return a new array containing the places in which the string pops up.
For example, if my variable contents were ["Oranges","Apples","Bananas"] and my search was "n", the function would return [0,2]. I am a beginner with JavaScript so thorough explanation would be helpful.
Thanks!
function matchArray(array, pattern) {
var i,
len = array.length,
result = [];
for (i = 0; i < len; ++i) {
if (match(array[i], pattern)) {
result.push(i);
}
}
return result;
}
Underscorejs has a function that should take care of this for you. Just take a look at the filter function. It has the ability to return a new array with items that passed a truth test.
var aArray = _.filter(['asdf','ddd','afsf'], function(item){
return (item.indexOf('a') !== -1);
}

Javascript: Find out if we have at least two different values in an array

My aim is to find out if we have at least two different values in an array. How to find out this using pure javascript. By now, I use this funcion, who tells me if there are repeated values in an array...
function verificar(array)
{
var filtrado_arr = array.sort();
var resultados = [];
for (var i = 0; i < array.length - 1; i++) {
if (filtrado_arr[i + 1] == filtrado_arr[i]) {
resultados.push(filtrado_arr[i]);
}
}
if (resultados.length > 0)
{
alert("Repeated results"+resultados+".Unable to send to graph.");
return false;
}
else
{
alert("No repeated measures, we can set graph");
return true;
}
}
But this is not enought, of course.
Using a sort and an extra array seems like an overly expensive way to perform this simple task. What is wrong with the following?
function verificar(arr)
{
for (var i=1; i<arr.length; i++) {
if (arr[i-1] != arr[i])
return true;
}
return false;
}
function verificar(array) {
for(i=1; i<array.length; i++){
if(array[0] !== array[i])
return true;
}
return false;
}
Your code implies that you want to check for duplicates, not verify that you have at leas one unique pair of values as stated in your question.
I'd just add another method to the pool of answers: checking for duplicates using Array.some():
function hasDuplicates(array){
return array.some( function( elm, idx ){
return array.lastIndexOf( elm ) > idx;
});
}
console.log( hasDuplicates([3,4,1,2]) ); // false
console.log( hasDuplicates([3,4,1,3,2]) ); // true
Just check the first value of the array "filitrado_arr[0]" against all of the other values in the array. If one of the values matches the first value of array, you know that there is a repeated value. Here is an example of how you could implement that logic:
function verificar(array){
var repeats = false
for (var i = 1; i < array.length; i++) {
if (array[0] == array[i]) {
repeats = true;
return repeats
}
}
}
However this answer matches the goal implied by the alerts in your original function not the goal in the question itself.

Categories