Count the occurrence of every alphabet from a string in Javascript - javascript

I have seen similar questions like this asked before, such as counting characters in a given string. However when it comes to comparing given string to the letters of the alphabet and returning an object with occurences such as:
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
const sampleString = "a bee";
const results = {
a: 1,
b: 1,
c: 0,
d: 0,
e: 2,
f: 0,
...
}

We can use Array.reduce(),
to count letters in the sampleString.
We start by creating a letterMap to specify all valid letters to be counted.
In the reduce loop, we only increment letters that are present in the letterMap, using the (c in acc) expression.
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
const sampleString = "a bee";
const letterMap = letters.reduce((acc, c) => {
acc[c] = 0;
return acc;
}, {});
const result = [...sampleString].reduce((acc, c) => {
if (c in acc) acc[c]++;
return acc;
}, letterMap);
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's another way, using just one loop, again using Array.reduce(), this assumes we don't wish to count whitespace:
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
const sampleString = "a bee";
const result = [...letters, ...sampleString].reduce((acc, c) => {
if (c in acc) {
acc[c]++;
} else if (c.trim()) {
acc[c] = 0;
}
return acc;
}, {});
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

I like using Object.fromEntries for this:
const sampleString = "a bee";
const result = Object.fromEntries(Array.from("abcdefghijklmnopqrstuvwxyz", ch => [ch, 0]));
for (let ch of sampleString)
if (ch in result) result[ch]++;
console.log(result);

Using Array.reduce and String.match may be an idea. So, for each letter of letters, use match (length) to determine the frequency of the letter in the given sample.
const letters = `abcdefghijklmnopqrstuvwxyz`.split(``);
const freq = (chr, sample) => (sample.match(RegExp(chr, `g`)) || []).length;
const result = letters.reduce( (acc, chr) =>
({...acc, [chr]: freq(chr, acc.sample)}), {sample: "a bee"});
console.log(result);

Or a more undestandable way
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
const sampleString = "a bee";
results = {};
sampleString.forEach(letter=>{
if(letters.includes(letter)) {
if(results[letter] === undefined) results[letter]=0;
results[letter]++;
}
});

making the value as a key is a bad practice, because it will be hard to read, I recommend to call it what it is:
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
const tempString = "This is a string.";
let results = []
letters.forEach( char =>{
const count = countCharacterOccurences(tempString, char)
results.push({ alphabet: char, count })
})
function countCharacterOccurences(string, char) {
return string.split(char).length - 1;
}
console.log(results)
//filter alphabet with value
const alphabetWithCount = results.filter( result => {
return result.count > 0
})
console.log(alphabetWithCount)
//log alphabet only with counts
const alphabetOnlyWithCounts = alphabetWithCounts.map( alphabetWithCount => {
return alphabetWithCount.alphabet
})
console.log(alphabetOnlyWithCounts)
//

You can apply .reduce() on the letters directly and in each iteration use RegExp() to replace all letters that do not match the current letter and the count the remaining letters in the string, if any, with .length:
const letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
const sampleString = "a bee";
const results = letters.reduce((result,letter) => ({
...result,
[letter]: sampleString.replace(new RegExp(`[^${letter}]`,'g'), "").length
}), {});
console.log( results );
NOTE:
Please note that:
sr.replace(new RegExp('[^a]','g'), "").length
for example, is equivalent to:
sr.replace(/[^a]/g,"").length
See:
JavaScript: How many times a character occurs in a string?

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"]));

Find unique characters in a string and the count of their occurrences

I need to count the occurrence of characters in a given string and print out the unique characters and the number of how many times they appeared. So, for example, if I receive a string of 'HELLO' it should print out:
H: 1,
E: 1,
L: 2,
O: 1
This is a much-simplified version of a problem, but the answer should put me in the right direction. How can I approach this problem?
Thank you in advance.
This is more or less what it should look like in order to make it easier it prints it in JSON you can already convert it to String yourself if you want.
function count_occurrence(text = "") {
const array_from_text = text.split("");
const result = {};
Array.from(new Set(array_from_text)).forEach(word => {
const { length } = array_from_text.filter(w => w === word);
result[word] = length;
});
return result;
};
const occurences = count_occurence("HELLO");
console.log(occurences); // {H: 1, E: 1, L: 2, O: 1}
You could use Array.reduce() to get a count of each occurrence in the input word.
We convert the word to an array using the ... operator, then .reduce() to create an object with a property for each unique letter in the word.
const input = 'HELLO';
const result = [...input].reduce((acc, chr) => {
acc[chr] = (acc[chr] || 0) + 1;
return acc;
}, {});
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
const countChars = (str) => {
const charCount = {} ;
for (const c of [...str]) {
charCount[c] = (charCount[c] || 0) + 1 ;
}
return charCount ;
}
console.log(countChars('HELLO')) ; // {H: 1, E: 1, L: 2, O: 1}
My approach to this problem is:
let str = "HELLO";
// An object for the final result {character:count}
let counts = {};
// Loop through the str...
for (let index = 0; index < str.length; ++index) {
// Get each char
let ch = str.charAt(index);
// Get the count for that char
let count = counts[ch];
// If we have one, store that count plus one;
if (count) {
counts[ch] += 1;
} else {
// if not, store one
counts[ch] = 1;
}
// or more simply with ternary operator
// counts[ch] = count ? count + 1 : 1;.
}
console.log(counts);
Maybe the easiest answer is just split to char and put it into the map.
const count={}
"HELLO".split("").forEach(e=>{
count[e]??=0;
count[e]++;
})
count is what you want.
Use a dictionary like datastructure that gives you O(1) access and update times. In JS you can use an Object literat (not recommended) or a Map.
Iterate over the characters of your string and update the dictionary by incrementing the character count of the current character. If it isn't in your dictionary add it and set the count to one.
When done with the iteration, iterate over the keys of your dictionary, where the values are the the number of occurence of that specific character, and output them in any format of your liking.
const myStr = "Hello"
const myMap = new Map()
for (let c of myStr) {
if (myMap.has(c)) {
myMap.set(c, myMap.get(c)+1)
} else {
myMap.set(c, 1)
}
}
for (let k of myMap.keys()) {
console.log(`${k} occures ${myMap.get(k)} times`)
}

How to find the array from the string? [duplicate]

This question already has answers here:
Regular expression to extract text between square brackets
(15 answers)
Closed 1 year ago.
const str = "[1,2,3,4],5,6";
const getArray = (str) => {
...
return arr;
}
console.log(getArray(str)); //[1,2,3,4]
[1,2,3,4] is the expected array.
How can I get this one?
You can use regex for extracting the numbers enclosed by [] then run a map for sanitize. Check this-
const str = "[1,2,3,4],5,6";
const getArray = str => {
const regex = /(\[([^\]]+)\])/;
const match = regex.exec(str);
return match?.[2] ? match[2].split(',').map(x => x.trim()) : [];
}
console.log(getArray(str));
You can use the JavaScript method lastIndexOf() to find the [ and ].
This will give you all characters in-between [ and ].
Then you can use the split() method to convert the array.
Using map() and Number you can convert string to number
const str = "[1,2,3,4],5,6";
function getArray(str){
var myArray = str.substring(
str.lastIndexOf("[") + 1,
str.lastIndexOf("]")
);
return myArray.split(",").map(Number);
}
console.log(getArray(str));
This will give you an array of all arrays within your string.
const str = "[1,2,3,4],5,6";
const getArray = (str) => {
const ans = []
let stack = []
let isStack = true
for (let i = 0; i < str.length; i++) {
if (str[i] == '[') {
isStack = true;
} else if (str[i] == ']') {
isStack = false;
ans.push(stack)
stack = []
} else if (isStack && str[i] != ',') {
stack.push(parseInt(str[i]))
}
}
return ans;
}
console.log(getArray(str)) // [ [ 1, 2, 3, 4 ] ]
console.log(getArray(str)[0]) // [ 1, 2, 3, 4 ]
Assuming that str contains exactly one pair of enclosing square brackets:
const getArray = (str) => {
const idxStart = str.indexOf('[') + 1
const idxEnd = str.indexOf(']')
arr = str.slice(idxStart, idxEnd)
return arr.split(',')
}
You can use RegEx to match the string enclosed in []
const str = "[1,2,3,4],5,6";
const getArray = (str) => {
arr = str.match('/\[.*\]/')[0]
return arr;
}
console.log(getArray(str));

Script to group elements where every character is same to each other

For input:
["abc","def","okg","fed","bca"]
expected output should be:
["abc","bca"],["def","fed"],["okg"]
here "abc", "bca" and "def", "fed" contains same character and "okg" there is no element which contains these character
const arr = ["abc", "def", "okg", "fed", "bca"];
let find = (arr) => {
let res = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 1; j < arr.length; j++) {
if (arr[i].search(arr[j])) {
res.push(arr[j]);
}
}
}
return res;
}
console.log(find(arr))
A reduce will do the trick - it seems the shortest code here (apart from the one using lodash)
const arr = ["abc", "def", "okg", "fed", "bca"],
res = Object.values(arr.reduce((acc, ele) => {
const key = ele.split("").sort();
(acc[key] = acc[key] || []).push(ele)
return acc
}, {}))
console.log(res)
.search returns a number indicating the index of where the match was found. Check that the result isn't -1 instead of checking that the result is truthy. But...
.search isn't the right tool here anyway, because it won't find different combinations of the same character. You need a different approach. One way would be to create an object whose keys are the characters found, and the values are the number of occurrences, then use a sorted representation of that object as a key. For example, have both abc and bca turn into something like:
a,1-b,1-c,1
Iterate through the input array, generating a key for each string, and putting the string on an object with that key. At the end, take the object's values.
const strToKey = (str) => {
const grouped = {};
for (const char of str) {
grouped[char] = (grouped[char] || 0) + 1;
}
return Object.entries(grouped)
.sort((a, b) => a[0].localeCompare(b[0]))
.join('-');
};
let find = (arr) => {
const grouped = {};
for (const str of arr) {
const key = strToKey(str);
grouped[key] ??= [];
grouped[key].push(str);
}
return Object.values(grouped);
}
console.log(find(["abc", "def", "okg", "fed", "bca"]));
Another option, when creating the keys, instead of sorting the object afterwards, you could sort the string first:
const strToKey = (str) => {
const grouped = {};
for (const char of [...str].sort()) {
grouped[char] = (grouped[char] || 0) + 1;
}
return Object.entries(grouped).join('-');
};
let find = (arr) => {
const grouped = {};
for (const str of arr) {
const key = strToKey(str);
grouped[key] ??= [];
grouped[key].push(str);
}
return Object.values(grouped);
}
console.log(find(["abc", "def", "okg", "fed", "bca"]));
const input = ["abc","def","okg","fed","bca"]
function getSortedString (str) {
return [...str].sort().join('');
};
function groupBy(input) {
const grouped = [];
while(input.length) {
const nextInput = [];
const first = input[0];
const matched = [first];
for (let i = 1; i < input.length; i++) {
if(getSortedString(first) === getSortedString(input[i])) {
matched.push(input[i])
} else {
nextInput.push(input[i])
}
}
input = nextInput;
grouped.push(matched);
}
console.log(grouped);
}
groupBy(input);
Using Object.values and groupBy (from lodash), you can get a straightforward solution:
You group your array elements by their "sorted" form and then use Object.values to get the output array.
const arr = ["abc", "def", "okg", "fed", "bca"];
const sortString = (str) => str.split("").sort().join("")
const result = Object.values(_.groupBy(arr, sortString));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Create an arrays inside of another (main) array out of separated values

Problem
I have a string of numerical values separated by commas, and I want to include them in an array, and also each pair of them to be an array nested inside of the main array to be my drawing vertices.
How do I solve this problem?
Input:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
what I want them to be is:
Output:
var V_array = [[24,13],[47,20],[33,9],[68,18],[99,14],[150,33],[33,33],[34,15],[91,10]];
You could Split on every second comma in javascript and map the splitted pairs by converting the values to number.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
result = vertices.match(/[^,]+,[^,]+/g).map(s => s.split(',').map(Number));
console.log(result);
You can use the function reduce which operates over the splitted-string and check for the mod of each index.
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = str.split(',').reduce((a, s, i) => {
a.curr.push(Number(s));
if ((i + 1) % 2 === 0) {
a.arr.push(a.curr);
a.curr = [];
}
return a;
}, {arr: [], curr: []}).arr;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can split string into array and use reduce method. Take a look at the code below
const vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const numbers = vertices.split(',').map(Number)
const res = numbers
.reduce((acc, number, index, srcArray) => {
if (index % 2) {
return acc
}
return [
...acc,
[ number, srcArray[index + 1] ],
]
}, [])
console.log(res)
My two cents :) [new version]
let
str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
pair = [],
triplet = [];
JSON.parse(`[${str}]`).forEach((e,i)=>{pair.push( (i%2)?[pair.pop(),e]:e)})
console.log ( 'pair:', JSON.stringify(pair) )
// bonus => same idea for triplet :
JSON.parse(`[${str}]`).forEach((e,i)=>{
if ( (i%3)===2 ) triplet.push( [triplet.shift(),triplet.pop(),e] )
else if ( (i%3)===0 ) triplet.unshift(e)
else triplet.push(e)
})
console.log ( 'triplet:', JSON.stringify(triplet) )
You can use exec and JSON.parse
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
var array1;
var reg = /[^,]+,[^,]+/g
let op = []
while((array1 = reg.exec(vertices))!== null){
op.push(JSON.parse(`[${array1[0]}]`))
}
console.log(op)
Split on the , and use Array.reduce to group the pair into a new 2-D array:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").reduce((acc, ele, idx, arr) => {
if(idx === 0 || idx%2 === 0) {acc.push([+ele, +arr[idx + 1]]);}
return acc;
}, []);
console.log(pair);
Same can be done using Array.map, if the index is odd skip the element and filter out the undefined elements:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").map((ele, idx, arr) => (idx === 0 || idx%2 === 0) ? [+ele, +arr[idx + 1]] : undefined).filter(e => e);
console.log(pair);
My two cents :)
( thanks to Code Maniac for the idea of using JSON.parse )
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = JSON.parse(`[${str}]`).reduce((acc, cur, i) => {
if (i===1) return [[acc,cur]]
if (i%2) acc.push( [acc.pop(), cur] )
else acc.push( cur )
return acc
});
console.log ( result )
Here is my solution.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
vertices = vertices.split(",");
function convertToMultiArray (arr, length) {
var nArr = [];
while(arr.length > 0) {
nArr.push(arr.splice(0,length));
}
return nArr;
}
const res = convertToMultiArray(vertices, 2);
console.log('res', res);

Categories

Resources