How to increment a set of 2 letters using GAS? - javascript

How could this one be tweaked so that it could increment a set of two letters, so that it'd look like this:
AA, AB, AC...AZ, BA, BB, BC, etc
This is borrowed from tckmn, but it addresses one letter only.
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
function incrementChar(c) {
var index = alphabet.indexOf(c)
if (index == -1) return -1 // or whatever error value you want
return alphabet[index + 1 % alphabet.length]
}
Appreciate your help!

You just need two loops. One to iterate over the alphabet, and the second to iterate over the alphabet on each iteration of the first loop.
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const arr = [];
for (let i = 0; i < alphabet.length; i++) {
for (let j = 0; j < alphabet.length; j++) {
arr.push(`${alphabet[i]}${alphabet[j]}`);
}
}
console.log(arr);

In your situation, how about the following sample script?
const increase = s => {
const idx = [...s].reduce((c, e, i, a) => (c += (e.charCodeAt(0) - 64) * Math.pow(26, a.length - i - 1)), -1);
// Ref: https://stackoverflow.com/a/53678158
columnIndexToLetter = (n) => (a = Math.floor(n / 26)) >= 0 ? columnIndexToLetter(a - 1) + String.fromCharCode(65 + (n % 26)) : "";
return columnIndexToLetter(idx + 1);
};
const samples = ["A", "Z", "AA", "AZ", "ZZ"];
const res1 = samples.map(e => increase(e));
console.log(res1); // <--- [ 'B', 'AA', 'AB', 'BA', 'AAA' ]
// If you want to give one value, please use the following script.
const sample = "AA";
const res2 = increase(sample);
console.log(res2); // <--- AB
When this script is run, ["A", "Z", "AA", "AZ", "ZZ"] is converted to [ 'B', 'AA', 'AB', 'BA', 'AAA' ].
Reference:
map()

Related

Split Array object into chucks

I have this array in JS
initialArray = [A,B,C,C,D,E,F,F,G,K]
I want to split into:
chucks = [[A,B,C], [C,D,E,F], [F,G,K]]
Duplicate items are separators such as 'C' or 'F'
How to do this split in ES6?
You could reduce the array and add either the value or a new array, depending on the last value.
const
array = ['A','B','C','C','D','E','F','F','G','K'],
result = array.reduce((r, v, i, { [i - 1]: last }) => {
if (v === last) r.push([]);
r[r.length - 1].push(v);
return r;
}, [[]]);
console.log(result);
Fairly straight forward solution using a single for loop and holding the current chunk in a variable to push to.
const initialArray = ['A', 'A', 'B', 'C', 'C', 'D', 'E', 'F', 'F', 'G', 'K', 'K'];
const chunk_at_duplicate = (arr) => {
let chunk = [], res = [chunk];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === arr[i - 1]) {
res.push(chunk = []);
}
chunk.push(arr[i]);
}
return res;
};
console.log(chunk_at_duplicate(initialArray));
Edited: now is based on clones
const initialArray = ["A","B","C","C","D","E","F","F","G","K"];
const chunks = [];
let smallChunk = [];
let index = 0;
while (index < initialArray.length){
smallChunk.push(initialArray[index]);
if (initialArray[index+1] == initialArray[index]){
chunks.push(smallChunk);
smallChunk = [];
}
index++;
}
chunks.push(smallChunk);
console.log(chunks);
You can use for ... of together with initialArray.entries() as follows:
const initialArray = ["A","B","C","C","D","E","F","F","G","K"];
const chunks = [];
let start = 0
for(const [i,v] of initialArray.entries()) {
if(i > 0 && v === initialArray[i-1]) {
chunks.push( initialArray.slice(start,i) );
start = i;
} else if( i === initialArray.length - 1 ) {
chunks.push( initialArray.slice(start) );
}
}
console.log( chunks );

Sort Strings that have plus or minus

I am wondering if there is any way besides the very manual method of sorting the following (an example):
BBB+
BB
AA+
A-
BB-
A
B
AAA
We are trying to get the previous strings sorted as following :
AAA
AA+
A
A-
BBB+
BB
BB-
B
Is there any automatic sort that compares plus and minus putting plus first?
As I described in my comment, we can change the ratings into numbers so we can use basic sorting over complicated letter sorting.
const ratings = [
"BBB+",
"BB",
"AA+",
"A-",
"BB-",
"A",
"B",
"AAA"
];
// Each letter should be enough apart.
// Technically we can go for smaller numbers.
// But that can break if not carefull once we start using Moodys or S&P instead of Fitch.
// Since AA- is lower than AA, the - character has to be a negative number.
const weights = {
"A": 10000,
"B": 1000,
"C": 100,
"D": 10,
"+": 1,
"-": -1
};
const rating_values = ratings
.map( rating => ( {
rating,
"value": rating
.split( '' )
.map( character => weights[ character ] )
.reduce( ( a, b ) => a + b )
} ) );
const sorted_ratings = rating_values
.sort( ( a, b ) => b.value - a.value )
.map( obj => obj.rating );
console.log( sorted_ratings );
You could use three step sorting by splitting the letters from plus/minus sign.
For getting a sortable value for '+' and '-', this approach uses a weight for these characters and takes zero for not given signs.
Then
sort by the first character ascending,
sort by the all characters descending,
sort by weight ascending ('+' is smaller than '-')
var data = ['BBB+', 'BB', 'AA+', 'A-', 'BB-', 'A', 'B', 'AAA', 'AAA+', 'AAA-'];
data.sort((a, b) => {
var weight = { '+': -1, '-': 1 },
aa = a.split(/(?=[+\-])/),
bb = b.split(/(?=[+\-])/);
return aa[0][0].localeCompare(bb[0][0])
|| bb[0].localeCompare(aa[0])
|| (weight[aa[1]] || 0) - (weight[bb[1]] || 0);
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You want to do something like this perhaps:
let marks = {"A": 6, "B": 1, "+": 0.5, "-": -0.5}
customGradeSort = (mark1, mark2) => {
let value1 = mark1.split("").reduce((total, e) => {
total += marks[e];
return total;
}, 0);
let value2 = mark2.split("").reduce((total, e) => {
total += marks[e];
return total;
}, 0);
return (value2 - value1)
}
const myMarks = ["BBB+", "BB","AA+","A-","BB-","A","B","AAA"]
console.log(myMarks.sort(customGradeSort));
NOTE: take value of A such that A- > BBB+
In this case A- is 5.5 and BBB+ is 3.5.
I approached the problem by identyfying the sorting criteria. In this case they are in the following order: alphabetical, length and symbol.
And it brought me to the simple and short solution.
It also covers cases with different letters and their count:
const input = ["BBB+", "BB", "AA+", "A-", "BB-","A","B", "AAA"];
const sorted = input.sort((a,b) => {
const [ [ aStr, aSign ], [ bStr, bSign ] ] = [a,b].map(str => str.match(/[A-Z]+|[\+|\-]/gi));
const [aSignNum, bSignNum] = [aSign, bSign].map(a => a === "+" ? 1 : a === "-" ? -1 : 0);
const alphCriteria = aStr[0].localeCompare(bStr[0]);
const countCriteria = bStr.length - aStr.length;
const signCriteria = bSignNum - aSignNum;
return alphCriteria || countCriteria || signCriteria;
});
console.log(sorted);
Knowing I'm late, I will still contribute.
What I have done:
Create an object with weights for different characters
Sorted accordingly
let arr = ["A", "A-", "AA+", "AAA", "B", "BB", "BB-", "BBB+"];
let weights = {
A: 100,
B: 10,
'+': 1,
'-': -1
}
function getWeightSum(str) {
let weightSum = 0;
// Iterate all characters in string
// Accumulate total weight
for (let i = 0; i < str.length; i++) {
weightSum += weights[str[i]];
}
return weightSum;
}
function stringSort(a, b) {
let aInt = getWeightSum(a);
let bInt = getWeightSum(b);
if (aInt > bInt) return -1;
else if (aInt <= bInt) return 1;
else return 0;
}
console.log(arr.sort(stringSort));
My version is similar to the one from Nina (and I simplified by stealing that answer's localeCompare rather than my first fairly silly nested conditional operators.) But it captures a somewhat different description of each element, and uses destructuring and named values rather than array indices.
It also has a slightly different manner of handling +/-.
const compareRatings = (a, b) => {
// e.g. ['BBB', 'B', '+'] or ['BB', 'B', '']
const describe = r => r.match(/(([A-Z])\2*)([\-\+]?)/).slice(1)
const [base1, char1, ext1] = describe(a)
const [base2, char2, ext2] = describe(b)
return char1.localeCompare(char2) ||
base2.localeCompare(base1) ||
// Unicode: 43: '+', 44: ',', 45: '-'
((ext1 || ',').charCodeAt(0) - (ext2 || ',').charCodeAt(0))
}
const ratings = ['BBB+', 'BB', 'AA+', 'A-', 'BB-', 'A', 'B', 'AAA']
console.log(ratings.sort(compareRatings))
I prefer this technique to those that weight the letters because this way can extend to as many different letters as you like and to as many copies of each. It will not for instance sort "BBBBBBBBBBB" before "A", as many of those would, and it handles "XX-" just fine.

Javascript - Generating all combinations of elements in a single array (in pairs)

I've seen several similar questions about how to generate all possible combinations of elements in an array. But I'm having a very hard time figuring out how to write an algorithm that will only output combination pairs. Any suggestions would be super appreciated!
Starting with the following array (with N elements):
var array = ["apple", "banana", "lemon", "mango"];
And getting the following result:
var result = [
"apple banana"
"apple lemon"
"apple mango"
"banana lemon"
"banana mango"
"lemon mango"
];
I was trying out the following approach but this results in all possible combinations, instead only combination pairs.
var letters = splSentences;
var combi = [];
var temp= "";
var letLen = Math.pow(2, letters.length);
for (var i = 0; i < letLen ; i++){
temp= "";
for (var j=0;j<letters.length;j++) {
if ((i & Math.pow(2,j))){
temp += letters[j]+ " "
}
}
if (temp !== "") {
combi.push(temp);
}
}
Here are some functional programming solutions:
Using EcmaScript2019's flatMap:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.flatMap(
(v, i) => array.slice(i+1).map( w => v + ' ' + w )
);
console.log(result);
Before the introduction of flatMap (my answer in 2017), you would go for reduce or [].concat(...) in order to flatten the array:
var array = ["apple", "banana", "lemon", "mango"];
var result = array.reduce( (acc, v, i) =>
acc.concat(array.slice(i+1).map( w => v + ' ' + w )),
[]);
console.log(result);
Or:
var array = ["apple", "banana", "lemon", "mango"];
var result = [].concat(...array.map(
(v, i) => array.slice(i+1).map( w => v + ' ' + w ))
);
console.log(result);
A simple way would be to do a double for loop over the array where you skip the first i elements in the second loop.
let array = ["apple", "banana", "lemon", "mango"];
let results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (let i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (let j = i + 1; j < array.length; j++) {
results.push(`${array[i]} ${array[j]}`);
}
}
console.log(results);
Rewritten with ES5:
var array = ["apple", "banana", "lemon", "mango"];
var results = [];
// Since you only want pairs, there's no reason
// to iterate over the last element directly
for (var i = 0; i < array.length - 1; i++) {
// This is where you'll capture that last value
for (var j = i + 1; j < array.length; j++) {
results.push(array[i] + ' ' + array[j]);
}
}
console.log(results);
In my case, I wanted to get the combinations as follows, based on the size range of the array:
function getCombinations(valuesArray: String[])
{
var combi = [];
var temp = [];
var slent = Math.pow(2, valuesArray.length);
for (var i = 0; i < slent; i++)
{
temp = [];
for (var j = 0; j < valuesArray.length; j++)
{
if ((i & Math.pow(2, j)))
{
temp.push(valuesArray[j]);
}
}
if (temp.length > 0)
{
combi.push(temp);
}
}
combi.sort((a, b) => a.length - b.length);
console.log(combi.join("\n"));
return combi;
}
Example:
// variable "results" stores an array with arrays string type
let results = getCombinations(['apple', 'banana', 'lemon', ',mango']);
Output in console:
The function is based on the logic of the following documentation, more information in the following reference:
https://www.w3resource.com/javascript-exercises/javascript-function-exercise-3.php
if ((i & Math.pow(2, j)))
Each bit of the first value is compared with the second, it is taken as valid if it matches, otherwise it returns zero and the condition is not met.
Although solutions have been found, I post here an algorithm for general case to find all combinations size n of m (m>n) elements. In your case, we have n=2 and m=4.
const result = [];
result.length = 2; //n=2
function combine(input, len, start) {
if(len === 0) {
console.log( result.join(" ") ); //process here the result
return;
}
for (let i = start; i <= input.length - len; i++) {
result[result.length - len] = input[i];
combine(input, len-1, i+1 );
}
}
const array = ["apple", "banana", "lemon", "mango"];
combine( array, result.length, 0);
I ended up writing a general solution to this problem, which is functionally equivalent to nhnghia's answer, but I'm sharing it here as I think it's easier to read/follow and is also full of comments describing the algorithm.
/**
* Generate all combinations of an array.
* #param {Array} sourceArray - Array of input elements.
* #param {number} comboLength - Desired length of combinations.
* #return {Array} Array of combination arrays.
*/
function generateCombinations(sourceArray, comboLength) {
const sourceLength = sourceArray.length;
if (comboLength > sourceLength) return [];
const combos = []; // Stores valid combinations as they are generated.
// Accepts a partial combination, an index into sourceArray,
// and the number of elements required to be added to create a full-length combination.
// Called recursively to build combinations, adding subsequent elements at each call depth.
const makeNextCombos = (workingCombo, currentIndex, remainingCount) => {
const oneAwayFromComboLength = remainingCount == 1;
// For each element that remaines to be added to the working combination.
for (let sourceIndex = currentIndex; sourceIndex < sourceLength; sourceIndex++) {
// Get next (possibly partial) combination.
const next = [ ...workingCombo, sourceArray[sourceIndex] ];
if (oneAwayFromComboLength) {
// Combo of right length found, save it.
combos.push(next);
}
else {
// Otherwise go deeper to add more elements to the current partial combination.
makeNextCombos(next, sourceIndex + 1, remainingCount - 1);
}
}
}
makeNextCombos([], 0, comboLength);
return combos;
}
The best solutions I have found - https://lowrey.me/es6-javascript-combination-generator/
Uses ES6 generator functions, I adapted to TS. Most often you don't need all of the combinations at the same time. And I was getting annoyed by writing loops like for (let i=0; ... for let (j=i+1; ... for (let k=j+1... just to get combos one by one to test if I need to terminate the loops..
export function* combinations<T>(array: T[], length: number): IterableIterator<T[]> {
for (let i = 0; i < array.length; i++) {
if (length === 1) {
yield [array[i]];
} else {
const remaining = combinations(array.slice(i + 1, array.length), length - 1);
for (let next of remaining) {
yield [array[i], ...next];
}
}
}
}
usage:
for (const combo of combinations([1,2,3], 2)) {
console.log(combo)
}
output:
> (2) [1, 2]
> (2) [1, 3]
> (2) [2, 3]
Just to give an option for next who'll search it
const arr = ['a', 'b', 'c']
const combinations = ([head, ...tail]) => tail.length > 0 ? [...tail.map(tailValue => [head, tailValue]), ...combinations(tail)] : []
console.log(combinations(arr)) //[ [ 'a', 'b' ], [ 'a', 'c' ], [ 'b', 'c' ] ]
There are also this answer:
https://stackoverflow.com/a/64414875/19518308
The alghorithm is this answer generates all the possible sets of combination(or choose(n, k)) of n items within k spaces.
The algorhitm:
function choose(arr, k, prefix=[]) {
if (k == 0) return [prefix];
return arr.flatMap((v, i) =>
choose(arr.slice(i+1), k-1, [...prefix, v])
);
}
console.log(choose([0,1,2,3,4], 3));
I had a similar problem and this algorhitm is working very well for me.
Using map and flatMap the following can be done (flatMap is only supported on chrome and firefox)
var array = ["apple", "banana", "lemon", "mango"]
array.flatMap(x => array.map(y => x !== y ? x + ' ' + y : null)).filter(x => x)
I think it is an answer to all such questions.
/**
*
* Generates all combination of given Array or number
*
* #param {Array | number} item - Item accepts array or number. If it is array exports all combination of items. If it is a number export all combination of the number
* #param {number} n - pow of the item, if given value is `n` it will be export max `n` item combination
* #param {boolean} filter - if it is true it will just export items which have got n items length. Otherwise export all posible length.
* #return {Array} Array of combination arrays.
*
* Usage Example:
*
* console.log(combination(['A', 'B', 'C', 'D'], 2, true)); // [[ 'A','A' ], [ 'A', 'B' ]...] (16 items)
* console.log(combination(['A', 'B', 'C', 'D'])); // [['A', 'A', 'A', 'B' ],.....,['A'],] (340 items)
* console.log(comination(4, 2)); // all posible values [[ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 0, 0 ], [ 0, 1 ], [ 0, 2 ]...] (20 items)
*/
function combination(item, n) {
const filter = typeof n !=='undefined';
n = n ? n : item.length;
const result = [];
const isArray = item.constructor.name === 'Array';
const count = isArray ? item.length : item;
const pow = (x, n, m = []) => {
if (n > 0) {
for (var i = 0; i < count; i++) {
const value = pow(x, n - 1, [...m, isArray ? item[i] : i]);
result.push(value);
}
}
return m;
}
pow(isArray ? item.length : item, n);
return filter ? result.filter(item => item.length == n) : result;
}
console.log("#####first sample: ", combination(['A', 'B', 'C', 'D'], 2)); // with filter
console.log("#####second sample: ", combination(['A', 'B', 'C', 'D'])); // without filter
console.log("#####third sample: ", combination(4, 2)); // gives array with index number
Generating combinations of elements in an array is a lot like counting in a numeral system,
where the base is the number of elements in your array (if you account for the leading zeros that will be missing).
This gives you all the indices to your array (concatenated):
arr = ["apple", "banana", "lemon", "mango"]
base = arr.length
idx = [...Array(Math.pow(base, base)).keys()].map(x => x.toString(base))
You are only interested in pairs of two, so restrict the range accordingly:
range = (from, to) = [...Array(to).keys()].map(el => el + from)
indices = range => range.map(x => x.toString(base).padStart(2,"0"))
indices( range( 0, Math.pow(base, 2))) // range starts at 0, single digits are zero-padded.
Now what's left to do is map indices to values.
As you don't want elements paired with themselves and order doesn't matter,
those need to be removed, before mapping to the final result.
const range = (from, to) => [...Array(to).keys()].map(el => el + from)
const combinations = arr => {
const base = arr.length
return range(0, Math.pow(base, 2))
.map(x => x.toString(base).padStart(2, "0"))
.filter(i => !i.match(/(\d)\1/) && i === i.split('').sort().join(''))
.map(i => arr[i[0]] + " " + arr[i[1]])
}
console.log(combinations(["apple", "banana", "lemon", "mango"]))
With more than ten elements, toString() will return letters for indices; also, this will only work with up to 36 Elements.
Generating combinations is a classic problem. Here's my interpretation of that solution:
const combinations = (elements) => {
if (elements.length == 1) {
return [elements];
} else {
const tail = combinations(elements.slice(1));
return tail.reduce(
(combos, combo) => { combos.push([elements[0], ...combo]); return combos; },
[[elements[0]], ...tail]
);
}
};
const array = ["apple", "banana", "lemon", "mango"];
console.log(combinations(array));
Here is an non-mutating ES6 approach combining things (TS):
function combine (tail: any[], length: number, head: any[][] = [[]]): any[][] {
return tail.reduce((acc, tailElement) => {
const tailHeadVariants = head.reduce((acc, headElement: any[]) => {
const combination = [...headElement, tailElement]
return [...acc, combination]
}, [])
if (length === 1) return [...acc, tailHeadVariants]
const subCombinations = combine(tail.filter(t => t !== tailElement), length - 1, tailHeadVariants)
return [...acc, ...subCombinations]
}, [])
}
As this post is well indexed on Google under the keywords "generate all combinations", lots of people coming here simply need to generate all the unique combinations, regardless of the size of the output (not only pairs).
This post answers this need.
All unique combinations, without recursion:
const getCombos = async (a) => {
const separator = '';
const o = Object();
for (let i = 0; i < a.length; ++i) {
for (let j = i + 1; j <= a.length; ++j) {
const left = a.slice(i, j);
const right = a.slice(j, a.length);
o[left.join(separator)] = 1;
for (let k = 0; k < right.length; ++k) {
o[[...left, right[k]].join(separator)] = 1;
}
}
}
return Object.keys(o);
}
const a = ['a', 'b', 'c', 'd'];
const b = await getCombos(a);
console.log(b);
// (14) ['a', 'ab', 'ac', 'ad', 'abc', 'abd', 'abcd',
// 'b', 'bc', 'bd', 'bcd', 'c', 'cd', 'd']
This code splits the array into 2 sub arrays, left / right, then iterate over the right array to combine it with the left array. The left becomes bigger overtime, while the right becomes smaller. The result has only unique values.
Beating a dead horse a bit, but with smaller sets where recursion limit and performance is not a problem, the general combination generation can be done recursively with "recurse combinations containing the first element in given array" plus "recurse combinations not containing the first element". It gives quite compact implementation as a generator:
// Generator yielding k-item combinations of array a
function* choose(a, k) {
if(a.length == k) yield a;
else if(k == 0) yield [];
else {
for(let rest of choose(a.slice(1), k-1)) yield [a[0], ...rest];
for(let rest of choose(a.slice(1), k)) yield rest;
}
}
And even slightly shorter (and twice faster, 1 M calls of 7 choose 5 took 3.9 seconds with my MacBook) with function returning and array of combinations:
// Return an array of combinations
function comb(a, k) {
if(a.length === k) return [a];
else if(k === 0) return [[]];
else return [...comb(a.slice(1), k-1).map(c => [a[0], ...c]),
...comb(a.slice(1), k)];
}

Counting frequency of characters in a string using JavaScript [duplicate]

This question already has answers here:
Counting the occurrences / frequency of array elements
(39 answers)
Closed 4 months ago.
I need to write some kind of loop that can count the frequency of each letter in a string.
For example: "aabsssd"
output: a:2, b:1, s:3, d:1
Also want to map same character as property name in object. Any good idea how to do this?
I am not sure how to do it.
This is where I am so far:
var arr = ["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"];
function counter(x) {
var count = 0,
temp = [];
x = x.split('');
console.log(x);
for (var i = 0, len = x.length; i < len; i++) {
if (x[i] == "a") {
count++;
}
}
return count;
}
var a = "aabbddd";
console.log(counter(a));
Here you go:
function getFrequency(string) {
var freq = {};
for (var i=0; i<string.length;i++) {
var character = string.charAt(i);
if (freq[character]) {
freq[character]++;
} else {
freq[character] = 1;
}
}
return freq;
};
some ES6 syntax with reduce:
let counter = str => {
return str.split('').reduce((total, letter) => {
total[letter] ? total[letter]++ : total[letter] = 1;
return total;
}, {});
};
counter("aabsssd"); // => { a: 2, b: 1, s: 3, d: 1 }
Another solution:
function count (string) {
var count = {};
string.split('').forEach(function(s) {
count[s] ? count[s]++ : count[s] = 1;
});
return count;
}
With some ES6 features and short-circuiting:
const counter = s => [...s].reduce((a, c) => (a[c] = ++a[c] || 1) && a, {})
console.log(
counter("hello") // {h: 1, e: 1, l: 2, o: 1}
)
Here's another way:
const freqMap = s => [...s].reduce((freq,c) => {freq[c] = -~freq[c]; return freq} ,{})
Or, if you prefer a "for" loop:
function freqMap(s) {
freq={};
for (let c of s)
freq[c]=-~freq[c];
return freq;
}
e.g. freqMap("MaMaMia") returns Object{M : 3, a : 3, i : 1}
This method leverages the fact that in javascript, bitwise not on "undefined" gives -1, (whereas "undefined+1" gives NaN).
So, -~undefined is 1, -~1 is 2, -~2 is 3 etc.
We can thus iterate over the characters of the string, and simply increment freq[c] without any "if". The first time we encounter a character c, freq[c] will be undefined, so we set it to -~freq[c] which is 1. If we subsequently encounter c again, we again set freq[c] to -~freq[c], which will now be 2, etc.
Simple, elegant, concise.
More declarative way to get a word histogram will be to utilise reduce to iterate through letters and come up with a new object that contains letters as keys and frequencies as values.
function getFrequency(str) {
return str.split('').reduce( (prev, curr) => {
prev[curr] = prev[curr] ? prev[curr] + 1 : 1;
return prev;
}, {});
};
console.log(getFrequency('test')); // => {t: 2, e: 1, s: 1}
a leaner, functional solution:
using ES6 Arrows && Logical Operators:
const buildFreqDict = string =>
string.split('').reduce((freqDict, char) => {
freqDict[char] = (freqDict[char] || 0) + 1;
return freqDict;
}, {})
console.log(buildFreqDict("banana"))
Explained
split string into array of characters.
and then feed it into a reduce method (using method.chaining()).
if char is already logged in countDict then add 1 to it.
or if character not found in countDict then set it to 1.
return new values back up to reduce's accumulator object
NB: don't forget about including the third argument of .reduce(): in this case it is a {} (object literal) that serves to initialize the freqDict object.
for more info see Counting instances of values in an object half way down the page here: MDN Reduce
and for more info about using logical operators please see here: MDN Logical Operators
An easy way. In addition, its get you an alphabetically sorted list. It loops throught an arrray and evaluate if the character is already in the object: if false, the character is added to the object, if true, its frequency increase a unit.
const text= "Lorem ipsum dolor sit amet consectetur adipiscing"
const textAsArray = text.split('').sort()
let charactersList = {}
for (char of textAsArray) {
if (!charactersList[char]) {
charactersList[char]=1;
}
else {
charactersList[char]++
}
}
console.log(charactersList)
I have reviewed and I think that this adapts very well to the need they pose. I would like it to be in a single line but I don't know how to generate the object dynamically.
const uniqueCount=(arr)=>{
let rs ={};
arr.sort().join("").match(/(.)(\1*)/g).map(i=>rs[i[0]]=i.length);
return rs;
};
console.log(uniqueCount(["a","b","c","d","d","e","a","b","c","f","g","h","h","h","e","a"]));
//{ a: 3, b: 2, c: 2, d: 2, e: 2, f: 1, g: 1, h: 3 }
I find it very successful to use .match() and regex /(.)(\1*)/g as explained above.
If it is just a string, you just need to add a .split("") before and that's it.
One more version with sorting by alphabetically. This function works for both.
Frequency of characters by alphabetically sorted
Frequency of characters in order of occurrence
Caveat: Only works if whole string is in lowercase
function freqWithAlphabetTable(str, doNeedToSort) {
let cnt = new Array(26).fill(0), firstLowerCase = 97, output = {}
for (let i = 0; i < str.length; i++)
cnt[str[i].charCodeAt(0) - firstLowerCase]++ // filling the array with count at it's index
if (doNeedToSort) {
for (let i = 0; i < cnt.length; i++) {
if (cnt[i] !== 0)
output[String.fromCharCode(firstLowerCase)] = cnt[i]
firstLowerCase++;
}
} else {
for (let i = 0; i < str.length; i++) {
let letterIndexVal = cnt[str[i].charCodeAt(0) - firstLowerCase];
if (letterIndexVal != 0 ) {
output[str[i]] = letterIndexVal
letterIndexVal = 0 // replacing it with zero to avoid repetition
}
}
}
console.log(output);
return output;
}
for(i = strlen(string)var string = 'aabsssd';
var chars = new Array();
for(var i = 0; i < string.length; i++){
var char = string.charAt(i);
if(chars[char] == undefined){
chars[char] = 0;
}
chars[char]++;
}
console.log(chars);
Here's another option using underscore.js:
function charCount(str) {
return _(str.split('')).countBy(function(char) {
return char.toLowerCase();
});
}
charCount('aaabbbbdd') outputs Object {a: 3, b: 4, d: 2}
const recorrences = ['a', 'b', 'c', 'a', 'b','a']
.map(i => !!~i.indexOf('a'))
.filter(i => i)
.length;
console.log(`recorrences ${recorrences}`)
//recorrences 3
// Count frequency of characters in a string
// input: 'Hello, I'm Paul!'
// result: {
// H: 1,
// E: 1,
// L: 3,
// ... and so on ...
// }
const countChars = (string) => {
let charStats = {};
string = string.replace(' ', '').toUpperCase().split('');
string.forEach((char) => {
if (charStats[char]) {
charStats[char]++;
} else {
charStats[char] = 1;
}
});
return charStats;
};
Another Solution
function maxChar(str) {
const charMap = {};
let max = 0;
let maxChar = '';
for(let char of str){
if(charMap[char]){
charMap[char]++;
}else{
charMap[char] = 1;
}
}
for(let char in charMap){
if(charMap[char] > max){
max = charMap[char];
maxChar = char;
}
}
return maxChar;
}
===>
maxChar('355385')
"5"
var str = 'abcccdddd';
function maxCharCount(target) {
const chars = {};
let maxChar = '';
let maxValue = 1;
for (let char of target) {
chars[char] = chars[char] + 1 || 1;
}
return chars;
}
console.log(maxCharCount(str));
The same solution but refactored. So cool how we can solve this problem with so many different answers :)
function getFrequency(string) {
var freq = {};
for (let character in string) {
let char = string[character];
(freq[char]) ? freq[char]++ : freq[char] = 1
}
return freq;
};
You can use this. Just pass the string and it will return object with all the character frequency.
function buildCharMap(string) {
const charMap = {};
string.replace(/[^\w]/g, '').toLowerCase();
for (let char of string) {
charMap[char] = charMap[char] + 1 || 1;
}
return charMap;
}
[...str].map( char => map.get(char) ? map.set( char, map.get(char) + 1) : map.set(char,1) )
cheat code to count frequency of a char in a string is
let target = "e";
let string = " i want to see that person that came in here last";
let frequency = string.split(target).length - 1;
or all in one line
console.log(string.split("e").length - 1)
Everyone using split and reduce are over-complicating things.
string is an iterator so you can use a for/of loop to go over each letter - there's no need to split it into an array so reduce can use it. reduce is very useful for lots of things but it often seems like: "when all you have is a hammer everything looks like a nail". I think its used unnecessarily in many places.
Anyway...
Create a new object.
Loop over the string.
If there is no key in the object that corresponds to the current letter, add it and set it it to zero.
Increment it.
function counter(str) {
// Create an object
const obj = {};
// Loop through the string
for (const letter of str) {
// If the object doesn't have a `letter`
// property create one and set it to 0;
obj[letter] ??= 0;
// Increment the value
++obj[letter];
}
// Finally return the object
return obj;
}
const str = 'aabbddd';
console.log(counter(str));
Additional documentation
for/of
Logical nullish assignment

Finding longest string in array

Is there a short way to find the longest string in a string array?
Something like arr.Max(x => x.Length);?
Available since Javascript 1.8/ECMAScript 5 and available in most older browsers:
var longest = arr.reduce(
function (a, b) {
return a.length > b.length ? a : b;
}
);
Otherwise, a safe alternative:
var longest = arr.sort(
function (a, b) {
return b.length - a.length;
}
)[0];
A new answer to an old question: in ES6 you can do shorter:
Math.max(...(x.map(el => el.length)));
I would do something like this
var arr = [
'first item',
'second item is longer than the third one',
'third longish item'
];
var lgth = 0;
var longest;
for (var i = 0; i < arr.length; i++) {
if (arr[i].length > lgth) {
var lgth = arr[i].length;
longest = arr[i];
}
}
console.log(longest);
Maybe not the fastest, but certainly pretty readable:
function findLongestWord(array) {
var longestWord = "";
array.forEach(function(word) {
if(word.length > longestWord.length) {
longestWord = word;
}
});
return longestWord;
}
var word = findLongestWord(["The","quick","brown", "fox", "jumped", "over", "the", "lazy", "dog"]);
console.log(word); // result is "jumped"
The array function forEach has been supported since IE9+.
In ES6 this could be accomplished with a reduce() call in O(n) complexity as opposed to solutions using sort() which is O(nlogn):
const getLongestText = (arr) => arr.reduce(
(savedText, text) => (text.length > savedText.length ? text : savedText),
'',
);
console.log(getLongestText(['word', 'even-longer-word', 'long-word']))
var arr = [ 'fdgdfgdfg', 'gdfgf', 'gdfgdfhawsdgd', 'gdf', 'gdfhdfhjurvweadsd' ];
arr.sort(function (a, b) { return b.length - a.length })[0];
I was inspired of Jason's function and made a little improvements to it and got as a result rather fast finder:
function timo_longest(a) {
var c = 0, d = 0, l = 0, i = a.length;
if (i) while (i--) {
d = a[i].length;
if (d > c) {
l = i; c = d;
}
}
return a[l];
}
arr=["First", "Second", "Third"];
var longest = timo_longest(arr);
Speed results: http://jsperf.com/longest-string-in-array/7
I provide a functional+recursive approach. See comments to understand how it works:
const input1 = ['a', 'aa', 'aaa']
const input2 = ['asdf', 'qwer', 'zxcv']
const input3 = ['asdfasdf fdasdf a sd f', ' asdfsdf', 'asdfasdfds', 'asdfsdf', 'asdfsdaf']
const input4 = ['ddd', 'dddddddd', 'dddd', 'ddddd', 'ddd', 'dd', 'd', 'd', 'dddddddddddd']
// Outputs which words has the greater length
// greatestWord :: String -> String -> String
const greatestWord = x => y =>
x.length > y.length ? x : y
// Recursively outputs the first longest word in a series
// longestRec :: String -> [String] -> String
const longestRec = longestWord => ([ nextWord, ...words ]) =>
// ^^^^^^^^^^^^
// Destructuring lets us get the next word, and remaining ones!
nextWord // <-- If next word is undefined, then it won't recurse.
? longestRec (greatestWord (nextWord) (longestWord)) (words)
: longestWord
// Outputs the first longest word in a series
// longest :: [String] -> String
const longest = longestRec ('')
const output1 = longest (input1)
const output2 = longest (input2)
const output3 = longest (input3)
const output4 = longest (input4)
console.log ('output1: ', output1)
console.log ('output2: ', output2)
console.log ('output3: ', output3)
console.log ('output4: ', output4)
function max( input ) {
return input.reduce((a, b) => a.length <= b.length ? b : a)
}
I see the shortest solution
function findLong(s){
return Math.max.apply(null, s.split(' ').map(w => w.length));
}
If your string is already split into an array, you'll not need the split part.
function findLongestWord(str) {
str = str.split(' ');
var longest = 0;
for(var i = 0; i < str.length; i++) {
if(str[i].length >= longest) {
longest = str[i].length;
}
}
return longest;
}
findLongestWord("The quick brown fox jumped over the lazy dog");
In case you expect more than one maximum this will work:
_.maxBy(Object.entries(_.groupBy(x, y => y.length)), y => parseInt(y[0]))[1]
It uses lodash and returns an array.
With ES6 and it support a duplicate string
var allLongestStrings = arrayOfStrings => {
let maxLng = Math.max(...arrayOfStrings.map( elem => elem.length))
return arrayOfStrings.filter(elem => elem.length === maxLng)
}
let arrayOfStrings = ["aba", "aa", "ad", "vcd","aba"]
console.log(allLongestStrings(arrayOfStrings))
I would do something like this:
function findLongestWord(str) {
var array = str.split(" ");
var maxLength=array[0].length;
for(var i=0; i < array.length; i++ ) {
if(array[i].length > maxLength) maxLength = array[i].length
}
return maxLength;
}
findLongestWord("What if we try a super-long word such as otorhinolaryngology");
Modern browsers support a for...of loop. The fastest and shortest way to solve this problem in Chrome, Safari, Edge, and Firefox is also the clearest:
let largest = '';
for (let item of arr) {
if (item.length > largest.length) largest = item
}
In IE, you can use Array.forEach; that's still faster and clearer than sorting or reducing the array.
var largest = '';
arr.forEach(function(item) {
if (item.length > largest.length) largest = item
});
If you want to know the INDEX of the longest item:
var longest = arr.reduce(
(a, b, i) => arr[a].length < b.length ? i : a,
0
);
(which can be a one-liner for those that love that stuff.... but it's split up here for readabilty)

Categories