I want to write a function to get all of the permutations of the array elements.
I do not want you to write the function for me! I want to work through it myself. Please do me the favor of letting me work through it.
Input: [1,2,3]
Output: 123, 132, 213, 231, 312, 321.
I believe the output should be an array of length factorial(input.length). So if the input is a three element array, the output should contain 3*2 elements. A four element input array would result in an array length of 24 (4*3*2) elements, with each element of the input array being the first element of a six element array.
Input: ['a','bo']
Output: ['abo', 'boa']
Input: [1,2,3,4]
Output:
[
1234 1243 1324 1342 1423 1432
2134 2143 2314 2341 2413 2431
3124 3142 3214 3241 3412 3421
4123 4132 4213 4231 4312 4321
]
(commas omitted from the output pseudocode).
Here is what I have so far. It isn't working. I think I need a nested loop.
function getWordPermutations(words) {
const len = words.length;
const factorial = n => !(n > 1) ? 1 : factorial(n - 1) * n;
let r = [words.join("")];
for(let i = 0; i < words.length; i++) {
let tmp = words[i];
let nextIndex = words[i+1] ? i+1 : words.length-1 - i;
words[i] = words[nextIndex];
words[nextIndex] = tmp;
r.push(words.join(""))
}
console.log(r);
return r;
}
getWordPermutations([1,2,3]); // Result: ["123", "213", "231", "132"]
It doesn't complete all of the permutations. Can you ask me some questions to get me to think about it.
Taken from, https://github.com/mission-peace/interview/blob/master/src/com/interview/recursion/StringPermutation.java
and re-implemented in Javascript.
function permute(input) {
let countMap = new Map();
for (ch of input.split("")) {
//console.log(ch, countMap.has(ch));
if (countMap.has(ch)) {
let inc = countMap.get(ch) + 1;
countMap.set(ch, inc);
} else {
countMap.set(ch, 1);
}
}
//console.log(countMap);
str = new Array(countMap.size); //char[countMap.size()];
count = new Array(countMap.size); //int[countMap.size];
index = 0;
for (let [key, val] of countMap.entries()) {
//console.log(key, val);
str[index] = key;
count[index] = val;
index++;
}
//console.log(str, count);
resultList = new Array();
result = new Array(input.length); //char[input.length]
permuteUtil(str, count, result, 0, resultList);
return resultList;
}
function permuteUtil(str, count, result, level, resultList) {
if (level == result.length) {
resultList.push(result.join("")); //resultList.add(new String(result));
return;
}
for (let i = 0; i < str.length; i++) {
if (count[i] == 0) {
continue;
}
result[level] = str[i];
count[i]--;
permuteUtil(str, count, result, level + 1, resultList);
count[i]++;
}
}
permute("AABC").forEach((s) => console.log(s));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The output is as expected:
AABC
AACB
ABAC
ABCA
ACAB
ACBA
BAAC
BACA
BCAA
CAAB
CABA
CBAA
If you want a dynamic input and output via HTML and to allow more than just single characters then (https://codepen.io/Alexander9111/pen/abOMpgz):
For your specific examples, we just need to enter a comma-separated list like "1,2,3" into the HTML input tag:
function permute(input) {
let countMap = new Map();
const input_arr = input.split(",");
for (ch of input_arr) {
//console.log(ch, countMap.has(ch));
if (countMap.has(ch)) {
let inc = countMap.get(ch) + 1;
countMap.set(ch, inc);
} else {
countMap.set(ch, 1);
}
}
//console.log(countMap);
str = new Array(countMap.size); //char[countMap.size()];
count = new Array(countMap.size); //int[countMap.size];
index = 0;
for (let [key, val] of countMap.entries()) {
//console.log(key, val);
str[index] = key;
count[index] = val;
index++;
}
//console.log(str, count);
resultList = new Array();
result = new Array(input_arr.length); //char[input.length]
permuteUtil(str, count, result, 0, resultList);
return resultList;
}
function permuteUtil(str, count, result, level, resultList) {
if (level == result.length) {
resultList.push(result.join(',')); //resultList.add(new String(result));
return;
}
for (let i = 0; i < str.length; i++) {
if (count[i] == 0) {
continue;
}
result[level] = str[i];
count[i]--;
permuteUtil(str, count, result, level + 1, resultList);
count[i]++;
}
}
//permute("1,2,3").forEach((s) => console.log(s));
const output_tag = document.getElementById("output");
const input_tag = document.getElementById("input");
const go = document.getElementById("go");
go.addEventListener("click", (e) => {
output_tag.innerText = permute(String(input_tag.value)).join("\n");
});
output_tag.innerText = permute(String(input_tag.value)).join("\n");
<h3>Input (comma separated):</h3>
<input id="input" value="1,2,3">
<br /><br />
<button id="go">Go</button>
<br>
<h3>Output:</h3>
<div id="output"></div>
I'm looking for an elegant way of determining which element has the highest occurrence (mode) in a JavaScript array.
For example, in
['pear', 'apple', 'orange', 'apple']
the 'apple' element is the most frequent one.
This is just the mode. Here's a quick, non-optimized solution. It should be O(n).
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
There have been some developments in javascript since 2009 - I thought I'd add another option. I'm less concerned with efficiency until it's actually a problem so my definition of "elegant" code (as stipulated by the OP) favours readability - which is of course subjective...
function mode(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
mode(['pear', 'apple', 'orange', 'apple']); // apple
In this particular example, should two or more elements of the set have equal occurrences then the one that appears latest in the array will be returned. It's also worth pointing out that it will modify your original array - which can be prevented if you wish with an Array.slice call beforehand.
Edit: updated the example with some ES6 fat arrows because 2015 happened and I think they look pretty... If you are concerned with backwards compatibility you can find this in the revision history.
As per George Jempty's request to have the algorithm account for ties, I propose a modified version of Matthew Flaschen's algorithm.
function modeString(array) {
if (array.length == 0) return null;
var modeMap = {},
maxEl = array[0],
maxCount = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
maxEl += "&" + el;
maxCount = modeMap[el];
}
}
return maxEl;
}
This will now return a string with the mode element(s) delimited by a & symbol. When the result is received it can be split on that & element and you have your mode(s).
Another option would be to return an array of mode element(s) like so:
function modeArray(array) {
if (array.length == 0) return null;
var modeMap = {},
maxCount = 1,
modes = [];
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
return modes;
}
In the above example you would then be able to handle the result of the function as an array of modes.
Based on Emissary's ES6+ answer, you could use Array.prototype.reduce to do your comparison (as opposed to sorting, popping and potentially mutating your array), which I think looks quite slick.
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
I'm defaulting to null, which won't always give you a truthful response if null is a possible option you're filtering for, maybe that could be an optional second argument
The downside, as with various other solutions, is that it doesn't handle 'draw states', but this could still be achieved with a slightly more involved reduce function.
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
if(b[k]) b[k]++; else b[k]=1;
if(maxi < b[k]) { max=k; maxi=b[k] }
}
As I'm using this function as a quiz for the interviewers, I post my solution:
const highest = arr => (arr || []).reduce( ( acc, el ) => {
acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1
acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el
return acc
}, { k:{} }).max
const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2]
console.log(highest(test))
Trying out a declarative approach here. This solution builds an object to tally up the occurrences of each word. Then filters the object down to an array by comparing the total occurrences of each word to the highest value found in the object.
const arr = ['hello', 'world', 'hello', 'again'];
const tally = (acc, x) => {
if (! acc[x]) {
acc[x] = 1;
return acc;
}
acc[x] += 1;
return acc;
};
const totals = arr.reduce(tally, {});
const keys = Object.keys(totals);
const values = keys.map(x => totals[x]);
const results = keys.filter(x => totals[x] === Math.max(...values));
This solution has O(n) complexity:
function findhighestOccurenceAndNum(a) {
let obj = {};
let maxNum, maxVal;
for (let v of a) {
obj[v] = ++obj[v] || 1;
if (maxVal === undefined || obj[v] > maxVal) {
maxNum = v;
maxVal = obj[v];
}
}
console.log(maxNum + ' has max value = ' + maxVal);
}
findhighestOccurenceAndNum(['pear', 'apple', 'orange', 'apple']);
For the sake of really easy to read, maintainable code I share this:
function getMaxOcurrences(arr = []) {
let item = arr[0];
let ocurrencesMap = {};
for (let i in arr) {
const current = arr[i];
if (ocurrencesMap[current]) ocurrencesMap[current]++;
else ocurrencesMap[current] = 1;
if (ocurrencesMap[item] < ocurrencesMap[current]) item = current;
}
return {
item: item,
ocurrences: ocurrencesMap[item]
};
}
Hope it helps someone ;)!
Here’s the modern version using built-in maps (so it works on more than things that can be converted to unique strings):
'use strict';
const histogram = iterable => {
const result = new Map();
for (const x of iterable) {
result.set(x, (result.get(x) || 0) + 1);
}
return result;
};
const mostCommon = iterable => {
let maxCount = 0;
let maxKey;
for (const [key, count] of histogram(iterable)) {
if (count > maxCount) {
maxCount = count;
maxKey = key;
}
}
return maxKey;
};
console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
Time for another solution:
function getMaxOccurrence(arr) {
var o = {}, maxCount = 0, maxValue, m;
for (var i=0, iLen=arr.length; i<iLen; i++) {
m = arr[i];
if (!o.hasOwnProperty(m)) {
o[m] = 0;
}
++o[m];
if (o[m] > maxCount) {
maxCount = o[m];
maxValue = m;
}
}
return maxValue;
}
If brevity matters (it doesn't), then:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
return mV;
}
If non–existent members are to be avoided (e.g. sparse array), an additional hasOwnProperty test is required:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
if (a.hasOwnProperty(i)) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
}
return mV;
}
getMaxOccurrence([,,,,,1,1]); // 1
Other answers here will return undefined.
Here is another ES6 way of doing it with O(n) complexity
const result = Object.entries(
['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => {
if (previous[current] === undefined) previous[current] = 1;
else previous[current]++;
return previous;
}, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0];
console.log("Max value : " + result);
function mode(arr){
return arr.reduce(function(counts,key){
var curCount = (counts[key+''] || 0) + 1;
counts[key+''] = curCount;
if (curCount > counts.max) { counts.max = curCount; counts.mode = key; }
return counts;
}, {max:0, mode: null}).mode
}
Another JS solution from: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php
Can try this too:
let arr =['pear', 'apple', 'orange', 'apple'];
function findMostFrequent(arr) {
let mf = 1;
let m = 0;
let item;
for (let i = 0; i < arr.length; i++) {
for (let j = i; j < arr.length; j++) {
if (arr[i] == arr[j]) {
m++;
if (m > mf) {
mf = m;
item = arr[i];
}
}
}
m = 0;
}
return item;
}
findMostFrequent(arr); // apple
This solution can return multiple elements of an array in case of a tie. For example, an array
arr = [ 3, 4, 3, 6, 4, ];
has two mode values: 3 and 6.
Here is the solution.
function find_mode(arr) {
var max = 0;
var maxarr = [];
var counter = [];
var maxarr = [];
arr.forEach(function(){
counter.push(0);
});
for(var i = 0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
if(arr[i]==arr[j])counter[i]++;
}
}
max=this.arrayMax(counter);
for(var i = 0;i<arr.length;i++){
if(counter[i]==max)maxarr.push(arr[i]);
}
var unique = maxarr.filter( this.onlyUnique );
return unique;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
};
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
const frequence = (array) =>
array.reduce(
(acc, item) =>
array.filter((v) => v === acc).length >=
array.filter((v) => v === item).length
? acc
: item,
null
);
frequence([1, 1, 2])
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17],
c = {}, // counters
s = []; // sortable array
for (var i=0; i<array.length; i++) {
c[array[i]] = c[array[i]] || 0; // initialize
c[array[i]]++;
} // count occurrences
for (var key in c) {
s.push([key, c[key]])
} // build sortable array from counters
s.sort(function(a, b) {return b[1]-a[1];});
var firstMode = s[0][0];
console.log(firstMode);
Here is my solution to this problem but with numbers and using the new 'Set' feature. Its not very performant but i definitely had a lot of fun writing this and it does support multiple maximum values.
const mode = (arr) => [...new Set(arr)]
.map((value) => [value, arr.filter((v) => v === value).length])
.sort((a,b) => a[1]-b[1])
.reverse()
.filter((value, i, a) => a.indexOf(value) === i)
.filter((v, i, a) => v[1] === a[0][1])
.map((v) => v[0])
mode([1,2,3,3]) // [3]
mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]
By the way do not use this for production this is just an illustration of how you can solve it with ES6 and Array functions only.
const mode = (str) => {
return str
.split(' ')
.reduce((data, key) => {
let counter = data.map[key] + 1 || 1
data.map[key] = counter
if (counter > data.counter) {
data.counter = counter
data.mode = key
}
return data
}, {
counter: 0,
mode: null,
map: {}
})
.mode
}
console.log(mode('the t-rex is the greatest of them all'))
Here is my solution :-
function frequent(number){
var count = 0;
var sortedNumber = number.sort();
var start = number[0], item;
for(var i = 0 ; i < sortedNumber.length; i++){
if(start === sortedNumber[i] || sortedNumber[i] === sortedNumber[i+1]){
item = sortedNumber[i]
}
}
return item
}
console.log( frequent(['pear', 'apple', 'orange', 'apple']))
Try it too, this does not take in account browser version.
function mode(arr){
var a = [],b = 0,occurrence;
for(var i = 0; i < arr.length;i++){
if(a[arr[i]] != undefined){
a[arr[i]]++;
}else{
a[arr[i]] = 1;
}
}
for(var key in a){
if(a[key] > b){
b = a[key];
occurrence = key;
}
}
return occurrence;
}
alert(mode(['segunda','terça','terca','segunda','terça','segunda']));
Please note that this function returns latest occurence in the array
when 2 or more entries appear same number of times!
With ES6, you can chain the method like this:
function findMostFrequent(arr) {
return arr
.reduce((acc, cur, ind, arr) => {
if (arr.indexOf(cur) === ind) {
return [...acc, [cur, 1]];
} else {
acc[acc.indexOf(acc.find(e => e[0] === cur))] = [
cur,
acc[acc.indexOf(acc.find(e => e[0] === cur))][1] + 1
];
return acc;
}
}, [])
.sort((a, b) => b[1] - a[1])
.filter((cur, ind, arr) => cur[1] === arr[0][1])
.map(cur => cur[0]);
}
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple']));
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple', 'pear']));
If two elements have the same occurrence, it will return both of them. And it works with any type of element.
// O(n)
var arr = [1, 2, 3, 2, 3, 3, 5, 6];
var duplicates = {};
max = '';
maxi = 0;
arr.forEach((el) => {
duplicates[el] = duplicates[el] + 1 || 1;
if (maxi < duplicates[el]) {
max = el;
maxi = duplicates[el];
}
});
console.log(max);
I came up with a shorter solution, but it's using lodash. Works with any data, not just strings. For objects can be used:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el.someUniqueProp)), arr => arr.length)[0];
This is for strings:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el)), arr => arr.length)[0];
Just grouping data under a certain criteria, then finding the largest group.
Here is my way to do it so just using .filter.
var arr = ['pear', 'apple', 'orange', 'apple'];
function dup(arrr) {
let max = { item: 0, count: 0 };
for (let i = 0; i < arrr.length; i++) {
let arrOccurences = arrr.filter(item => { return item === arrr[i] }).length;
if (arrOccurences > max.count) {
max = { item: arrr[i], count: arrr.filter(item => { return item === arrr[i] }).length };
}
}
return max.item;
}
console.log(dup(arr));
Easy solution !
function mostFrequentElement(arr) {
let res = [];
for (let x of arr) {
let count = 0;
for (let i of arr) {
if (i == x) {
count++;
}
}
res.push(count);
}
return arr[res.indexOf(Math.max(...res))];
}
array = [13 , 2 , 1 , 2 , 10 , 2 , 1 , 1 , 2 , 2];
let frequentElement = mostFrequentElement(array);
console.log(`The frequent element in ${array} is ${frequentElement}`);
Loop on all element and collect the Count of each element in the array that is the idea of the solution
Here is my solution :-
const arr = [
2, 1, 10, 7, 10, 3, 10, 8, 7, 3, 10, 5, 4, 6, 7, 9, 2, 2, 2, 6, 3, 7, 6, 9, 8,
9, 10, 8, 8, 8, 4, 1, 9, 3, 4, 5, 8, 1, 9, 3, 2, 8, 1, 9, 6, 3, 9, 2, 3, 5, 3,
2, 7, 2, 5, 4, 5, 5, 8, 4, 6, 3, 9, 2, 3, 3, 10, 3, 3, 1, 4, 5, 4, 1, 5, 9, 6,
2, 3, 10, 9, 4, 3, 4, 5, 7, 2, 7, 2, 9, 8, 1, 8, 3, 3, 3, 3, 1, 1, 3,
];
function max(arr) {
let newObj = {};
arr.forEach((d, i) => {
if (newObj[d] != undefined) {
++newObj[d];
} else {
newObj[d] = 0;
}
});
let nwres = {};
for (let maxItem in newObj) {
if (newObj[maxItem] == Math.max(...Object.values(newObj))) {
nwres[maxItem] = newObj[maxItem];
}
}
return nwres;
}
console.log(max(arr));
I guess you have two approaches. Both of which have advantages.
Sort then Count or Loop through and use a hash table to do the counting for you.
The hashtable is nice because once you are done processing you also have all the distinct elements. If you had millions of items though, the hash table could end up using a lot of memory if the duplication rate is low. The sort, then count approach would have a much more controllable memory footprint.
var mode = 0;
var c = 0;
var num = new Array();
var value = 0;
var greatest = 0;
var ct = 0;
Note: ct is the length of the array.
function getMode()
{
for (var i = 0; i < ct; i++)
{
value = num[i];
if (i != ct)
{
while (value == num[i + 1])
{
c = c + 1;
i = i + 1;
}
}
if (c > greatest)
{
greatest = c;
mode = value;
}
c = 0;
}
}
You can try this:
// using splice()
// get the element with the highest occurence in an array
function mc(a) {
var us = [], l;
// find all the unique elements in the array
a.forEach(function (v) {
if (us.indexOf(v) === -1) {
us.push(v);
}
});
l = us.length;
while (true) {
for (var i = 0; i < l; i ++) {
if (a.indexOf(us[i]) === -1) {
continue;
} else if (a.indexOf(us[i]) != -1 && a.length > 1) {
// just delete it once at a time
a.splice(a.indexOf(us[i]), 1);
} else {
// default to last one
return a[0];
}
}
}
}
// using string.match method
function su(a) {
var s = a.join(),
uelms = [],
r = {},
l,
i,
m;
a.forEach(function (v) {
if (uelms.indexOf(v) === -1) {
uelms.push(v);
}
});
l = uelms.length;
// use match to calculate occurance times
for (i = 0; i < l; i ++) {
r[uelms[i]] = s.match(new RegExp(uelms[i], 'g')).length;
}
m = uelms[0];
for (var p in r) {
if (r[p] > r[m]) {
m = p;
} else {
continue;
}
}
return m;
}
in fact, i have to treat a Arabic characters in javascript but i want to show you an example of what i want:
i have this char:
var char = "KASSAB";
i want to get an array of chars by replacing the letter "A" in "1" "2" "3" everytime, so if i have 2 letters "A" in one char, it must be like that:
K**1**SS**1**B,K**1**SS**2**B, K**1**SS**3**B, K**2**SS**1**B, K**2**SS**2**B, K**2**SS**3**B, K**3**SS**1**B, K**3**SS**2**B, K**3**SS**3**B
Any suggestions Please :( ?
This can be done using a recursive function, as follows:
var initialInput = 'KASSAB',
replaceables = ['A', 'a', 'á'],
replacements = ['**1**', '**2**', '**3**'];
function indexOfFirstReplaceable(input) {
var firstIndex = input.length;
for (var i in replaceables) {
var index = input.indexOf(replaceables[i]);
if (index !== -1) firstIndex = Math.min(firstIndex, index);
}
return firstIndex < input.length ? firstIndex : -1;
}
function replaceAllAs(input) {
var index = indexOfFirstReplaceable(input);
if (index === -1) return [input] // Nothing to replace
var outputs = [];
for (var i in replacements) {
var nextInput = input.substr(0, index) + replacements[i] + input.substr(index + 1);
outputs = outputs.concat(replaceAllAs(nextInput));
}
return outputs;
}
console.log(replaceAllAs(initialInput));
will output
["K**1**SS**1**B", "K**1**SS**2**B", "K**1**SS**3**B", "K**2**SS**1**B", "K**2**SS**2**B", "K**2**SS**3**B", "K**3**SS**1**B", "K**3**SS**2**B", "K**3**SS**3**B"]
EDIT:
Or if you want to be more functional, using map and reduce:
var initialInput = 'KASSAB',
replaceables = ['A', 'a', 'á'],
replacements = ['**1**', '**2**', '**3**'];
function indexOfFirstReplaceable(input) {
return replaceables.reduce(function (currentMin, replaceable) {
var index = input.indexOf(replaceable);
if (index === -1 || currentMin === -1) return Math.max(index, currentMin);
else return Math.min(currentMin, index);
}, -1);
}
function replaceAllAs(input) {
var index = indexOfFirstReplaceable(input);
if (index === -1) return [input]; // Nothing to replace
var outputs = replacements.map(function (replacement) {
var nextInput = input.substr(0, index) + replacement + input.substr(index + 1);
return replaceAllAs(nextInput)
});
return [].concat.apply([], outputs);
}
console.log(replaceAllAs(initialInput));
EDIT 2:
To accommodate the overlap of replaceables and replacements you need to specify the fromIndex when using indexOf:
var initialInput = 'KASSAB',
replaceables = ['A', 'a', 'á'],
replacements = ['A', 'a', 'á'];
function indexOfFirstReplaceable(input, fromIndex) {
return replaceables.reduce(function (currentMin, replaceable) {
var index = input.indexOf(replaceable, fromIndex);
if (index === -1 || currentMin === -1) return Math.max(index, currentMin);
else return Math.min(currentMin, index);
}, -1);
}
function replaceAllAs(input, fromIndex) {
var index = indexOfFirstReplaceable(input, fromIndex);
if (index === -1) return [input]; // Nothing to replace
var outputs = replacements.map(function (replacement) {
var nextInput = input.substr(0, index) + replacement + input.substr(index + 1);
return replaceAllAs(nextInput, index + 1);
});
return [].concat.apply([], outputs);
}
console.log(replaceAllAs(initialInput, 0));
will output
["KASSAB", "KASSaB", "KASSáB", "KaSSAB", "KaSSaB", "KaSSáB", "KáSSAB", "KáSSaB", "KáSSáB"]
I'm trying to compare two strings to see if they are anagrams.
My problem is that I'm only comparing the first letter in each string. For example, "Mary" and "Army" will return true but unfortunately so will "Mary" and Arms."
How can I compare each letter of both strings before returning true/false?
Here's a jsbin demo (click the "Console" tab to see the results"):
http://jsbin.com/hasofodi/1/edit
function compare (a, b) {
y = a.split("").sort();
z = b.split("").sort();
for (i=0; i<y.length; i++) {
if(y.length===z.length) {
if (y[i]===z[i]){
console.log(a + " and " + b + " are anagrams!");
break;
}
else {
console.log(a + " and " + b + " are not anagrams.");
break;
}
}
else {
console.log(a + " has a different amount of letters than " + b);
}
break;
}
}
compare("mary", "arms");
Instead of comparing letter by letter, after sorting you can join the arrays to strings again, and let the browser do the comparison:
function compare (a, b) {
var y = a.split("").sort().join(""),
z = b.split("").sort().join("");
console.log(z === y
? a + " and " + b + " are anagrams!"
: a + " and " + b + " are not anagrams."
);
}
If you want to write a function, without using inbuilt one, Check the below solution.
function isAnagram(str1, str2) {
if(str1 === str2) {
return true;
}
let srt1Length = str1.length;
let srt2Length = str2.length;
if(srt1Length !== srt2Length) {
return false;
}
var counts = {};
for(let i = 0; i < srt1Length; i++) {
let index = str1.charCodeAt(i)-97;
counts[index] = (counts[index] || 0) + 1;
}
for(let j = 0; j < srt2Length; j++) {
let index = str2.charCodeAt(j)-97;
if (!counts[index]) {
return false;
}
counts[index]--;
}
return true;
}
This considers case sensitivity and removes white spaces AND ignore all non-alphanumeric characters
function compare(a,b) {
var c = a.replace(/\W+/g, '').toLowerCase().split("").sort().join("");
var d = b.replace(/\W+/g, '').toLowerCase().split("").sort().join("");
return (c ===d) ? "Anagram":"Not anagram";
}
Quick one-liner solution with javascript functions - toLowerCase(), split(), sort() and join():
Convert input string to lowercase
Make array of the string with split()
Sort the array alphabetically
Now join the sorted array into a string using join()
Do the above steps to both strings and if after sorting strings are the same then it will be anargam.
// Return true if two strings are anagram else return false
function Compare(str1, str2){
if (str1.length !== str2.length) {
return false
}
return str1.toLowerCase().split("").sort().join("") === str2.toLowerCase().split("").sort().join("")
}
console.log(Compare("Listen", "Silent")) //true
console.log(Compare("Mary", "arms")) //false
No need for sorting, splitting, or joining. The following two options are efficient ways to go:
//using char array for fast lookups
const Anagrams1 = (str1 = '', str2 = '') => {
if (str1.length !== str2.length) {
return false;
}
if (str1 === str2) {
return true;
}
const charCount = [];
let startIndex = str1.charCodeAt(0);
for (let i = 0; i < str1.length; i++) {
const charInt1 = str1.charCodeAt(i);
const charInt2 = str2.charCodeAt(i);
startIndex = Math.min(charInt1, charInt2);
charCount[charInt1] = (charCount[charInt1] || 0) + 1;
charCount[charInt2] = (charCount[charInt2] || 0) - 1;
}
while (charCount.length >= startIndex) {
if (charCount.pop()) {
return false;
}
}
return true;
}
console.log(Anagrams1('afc','acf'))//true
console.log(Anagrams1('baaa','bbaa'))//false
console.log(Anagrams1('banana','bananas'))//false
console.log(Anagrams1('',' '))//false
console.log(Anagrams1(9,'hey'))//false
//using {} for fast lookups
function Anagrams(str1 = '', str2 = '') {
if (str1.length !== str2.length) {
return false;
}
if (str1 === str2) {
return true;
}
const lookup = {};
for (let i = 0; i < str1.length; i++) {
const char1 = str1[i];
const char2 = str2[i];
const remainingChars = str1.length - (i + 1);
lookup[char1] = (lookup[char1] || 0) + 1;
lookup[char2] = (lookup[char2] || 0) - 1;
if (lookup[char1] > remainingChars || lookup[char2] > remainingChars) {
return false;
}
}
for (let i = 0; i < str1.length; i++) {
if (lookup[str1[i]] !== 0 || lookup[str2[i]] !== 0) {
return false;
}
}
return true;
}
console.log(Anagrams('abc', 'cba'));//true
console.log(Anagrams('abcc', 'cbaa')); //false
console.log(Anagrams('abc', 'cde')); //false
console.log(Anagrams('aaaaaaaabbbbbb','bbbbbbbbbaaaaa'));//false
console.log(Anagrams('banana', 'ananab'));//true
Cleanest and most efficient solution for me
function compare(word1, word2) {
const { length } = word1
if (length !== word2.length) {
return false
}
const charCounts = {}
for (let i = 0; i < length; i++) {
const char1 = word1[i]
const char2 = word2[i]
charCounts[char1] = (charCounts[char1] || 0) + 1
charCounts[char2] = (charCounts[char2] || 0) - 1
}
for (const char in charCounts) {
if (charCounts[char]) {
return false
}
}
return true
}
I modified your function to work.
It will loop through each letter of both words UNTIL a letter doesn't match (then it knows that they AREN'T anagrams).
It will only work for words that have the same number of letters and that are perfect anagrams.
function compare (a, b) {
y = a.split("").sort();
z = b.split("").sort();
areAnagrams = true;
for (i=0; i<y.length && areAnagrams; i++) {
console.log(i);
if(y.length===z.length) {
if (y[i]===z[i]){
// good for now
console.log('up to now it matches');
} else {
// a letter differs
console.log('a letter differs');
areAnagrams = false;
}
}
else {
console.log(a + " has a different amount of letters than " + b);
areAnagrams = false;
}
}
if (areAnagrams) {
console.log('They ARE anagrams');
} else {
console.log('They are NOT anagrams');
}
return areAnagrams;
}
compare("mary", "arms");
A more modern solution without sorting.
function(s, t) {
if(s === t) return true
if(s.length !== t.length) return false
let count = {}
for(let letter of s)
count[letter] = (count[letter] || 0) + 1
for(let letter of t) {
if(!count[letter]) return false
else --count[letter]
}
return true;
}
function validAnagramOrNot(a, b) {
if (a.length !== b.length)
return false;
const lookup = {};
for (let i = 0; i < a.length; i++) {
let character = a[i];
lookup[character] = (lookup[character] || 0) + 1;
}
for (let i = 0; i < b.length; i++) {
let character = b[i];
if (!lookup[character]) {
return false;
} else {
lookup[character]--;
}
}
return true;
}
validAnagramOrNot("a", "b"); // false
validAnagramOrNot("aza", "zaa"); //true
Here's my contribution, I had to do this exercise for a class! I'm finally understanding how JS works, and as I was able to came up with a solution (it's not - by far - the best one, but it's ok!) I'm very happy I can share this one here, too! (although there are plenty solutions here already, but whatever :P )
function isAnagram(string1, string2) {
// first check: if the lenghts are different, not an anagram
if (string1.length != string2.length)
return false
else {
// it doesn't matter if the letters are capitalized,
// so the toLowerCase method ensures that...
string1 = string1.toLowerCase()
string2 = string2.toLowerCase()
// for each letter in the string (I could've used for each :P)
for (let i = 0; i < string1.length; i++) {
// check, for each char in string2, if they are NOT somewhere at string1
if (!string1.includes(string2.charAt(i))) {
return false
}
else {
// if all the chars are covered
// and the condition is the opposite of the previous if
if (i == (string1.length - 1))
return true
}
}
}
}
First of all, you can do the length check before the for loop, no need to do it for each character...
Also, "break" breaks the whole for loop. If you use "continue" instead of "break", it skips the current step.
That is why only the first letters are compared, after the first one it quits the for loop.
I hope this helps you.
function compare (a, b) {
y = a.split("").sort();
z = b.split("").sort();
if(y.length==z.length) {
for (i=0; i<y.length; i++) {
if (y[i]!==z[i]){
console.log(a + " and " + b + " are not anagrams!");
return false;
}
}
return true;
} else { return false;}}
compare("mary", "arms");
Make the function return false if the length between words differ and if it finds a character between the words that doesn't match.
// check if two strings are anagrams
var areAnagrams = function(a, b) {
// if length is not the same the words can't be anagrams
if (a.length != b.length) return false;
// make words comparable
a = a.split("").sort().join("");
b = b.split("").sort().join("");
// check if each character match before proceeding
for (var i = 0; i < a.length; i++) {
if ((a.charAt(i)) != (b.charAt(i))) {
return false;
}
}
// all characters match!
return true;
};
It is specially effective when one is iterating through a big dictionary array, as it compares the first letter of each "normalised" word before proceeding to compare the second letter - and so on. If one letter doesn't match, it jumps to the next word, saving a lot of time.
In a dictionary with 1140 words (not all anagrams), the whole check was done 70% faster than if using the method in the currently accepted answer.
an anagram with modern javascript that can be use in nodejs. This will take into consideration empty strings, whitespace and case-sensitivity. Basically takes an array or a single string as input. It relies on sorting the input string and then looping over the list of words and doing the same and then comparing the strings to each other. It's very efficient. A more efficient solution may be to create a trie data structure and then traversing each string in the list. looping over the two words to compare strings is slower than using the built-in string equality check.
The function does not allow the same word as the input to be considered an anagram, as it is not an anagram. ;) useful edge-case.
const input = 'alerting';
const list1 = 'triangle';
const list2 = ['', ' ', 'alerting', 'buster', 'integral', 'relating', 'no', 'fellas', 'triangle', 'chucking'];
const isAnagram = ((input, list) => {
if (typeof list === 'string') {
list = [list];
}
const anagrams = [];
const sortedInput = sortWord(input).toLowerCase();
const inputLength = sortedInput.length;
list.forEach((element, i) => {
if ( inputLength === element.length && input !== element ) {
const sortedElement = sortWord(element).toLowerCase();
if ( sortedInput === sortedElement) {
anagrams.push(element);
}
}
});
return anagrams;
})
const sortWord = ((word) => {
return word.split('').sort().join('');
});
console.log(`anagrams for ${input} are: ${isAnagram(input, list1)}.`);
console.log(`anagrams for ${input} are: ${isAnagram(input, list2)}.`);
Here is a simple algorithm:
1. Remove all unnecessary characters
2. make objects of each character
3. check to see if object length matches and character count matches - then return true
const stripChar = (str) =>
{
return str.replace(/[\W]/g,'').toLowerCase();
}
const charMap = str => {
let MAP = {};
for (let char of stripChar(str)) {
!MAP[char] ? (MAP[char] = 1) : MAP[char]++;
}
return MAP;
};
const anagram = (str1, str2) => {
if(Object.keys(charMap(str1)).length!==Object.keys(charMap(str2)).length) return false;
for(let char in charMap(str1))
{
if(charMap(str1)[char]!==charMap(str2)[char]) return false;
}
return true;
};
console.log(anagram("rail safety","!f%airy tales"));
I think this is quite easy and simple.
function checkAnagrams(str1, str2){
var newstr1 = str1.toLowerCase().split('').sort().join();
var newstr2 = str2.toLowerCase().split('').sort().join();
if(newstr1 == newstr2){
console.log("String is Anagrams");
}
else{
console.log("String is Not Anagrams");
}
}
checkAnagrams("Hello", "lolHe");
checkAnagrams("Indian", "nIndisn");
//The best code so far that checks, white space, non alphabets
//characters
//without sorting
function anagram(stringOne,stringTwo){
var newStringOne = ""
var newStringTwo = ''
for(var i=0; i<stringTwo.length; i++){
if(stringTwo[i]!= ' ' && isNaN(stringTwo[i]) == true){
newStringTwo = newStringTwo+stringTwo[i]
}
}
for(var i=0; i<stringOne.length; i++){
if(newStringTwo.toLowerCase().includes(stringOne[i].toLowerCase())){
newStringOne=newStringOne+stringOne[i].toLowerCase()
}
}
console.log(newStringOne.length, newStringTwo.length)
if(newStringOne.length==newStringTwo.length){
console.log("Anagram is === to TRUE")
}
else{console.log("Anagram is === to FALSE")}
}
anagram('ddffTTh####$', '#dfT9t#D##H$F')
function anagrams(str1,str2){
//spliting string into array
let arr1 = str1.split("");
let arr2 = str2.split("");
//verifying array lengths
if(arr1.length !== arr2.length){
return false;
}
//creating objects
let frqcounter1={};
let frqcounter2 ={};
// looping through array elements and keeping count
for(let val of arr1){
frqcounter1[val] =(frqcounter1[val] || 0) + 1;
}
for(let val of arr2){
frqcounter2[val] =(frqcounter2[val] || 0) + 1;
}
console.log(frqcounter1);
console.log(frqcounter2);
//loop for every key in first object
for(let key in frqcounter1){
//if second object does not contain same frq count
if(frqcounter2[key] !== frqcounter1[key]){
return false;
}
}
return true;
}
anagrams('anagrams','nagramas');
The fastest Algorithm
const isAnagram = (str1, str2) => {
if (str1.length !== str2.length) {
return false
}
const obj = {}
for (let i = 0; i < str1.length; i++) {
const letter = str1[i]
obj[letter] ? obj[letter] += 1 : obj[letter] = 1
}
for (let i = 0; i < str2.length; i++) {
const letter = str2[i]
if (!obj[letter]) {
return false
}
else {
obj[letter] -= 1
}
}
return true
}
console.log(isAnagram('lalalalalalalalala', 'laalallalalalalala'))
console.time('1')
isAnagram('lalalalalalalalala', 'laalallalalalalala') // about 0.050ms
console.timeEnd('1')
const anagram = (strA, strB) => {
const buildAnagram = (str) => {
const charObj = {};
for(let char of str.replace(/[^\w]/g).toLowerCase()) {
charObj[char] = charObj[char] + 1 || 1;
}
return charObj;
};
const strObjAnagramA = buildAnagram(strA);
const strObjAnagramB = buildAnagram(strB);
if(Object.keys(strObjAnagramA).length !== Object.keys(strObjAnagramB).length) {
console.log(strA + ' and ' + strB + ' is not an anagram');
return false;
}
for(let char in strObjAnagramA) {
if(strObjAnagramA[char] !== strObjAnagramB[char]) {
console.log(strA + ' and ' + strB + ' is not an anagram');
return false;
}
}
return true; } //console.log(anagram('Mary','Arms')); - false
Similar approach with filter function
const str1 = 'triangde'
const str2 = 'integral'
const st1 = str1.split('')
const st2 = str2.split('')
const item = st1.filter((v)=>!st2.includes(v))
const result = item.length === 0 ? 'Anagram' : 'Not anagram' + ' Difference - ' + item;
console.log(result)