I'm trying to figure out how to solve this kata on CodeWars.
Function high recieves a string and returns the word with the highest "score" according to which letters in the word are present. The letters receive a score based on their position in the alphabet. So a = 1 point, b = 2 points, c = 3 points, and so on.
I think it makes sense to create an object where all of the letters in the alphabet are assigned a value:
If the letter in the word appears in alphabetScore, that word will receive its "points" and continue on to the next letter in the word, increasing the total points of the word.
I have:
function high(string) {
let words = string.split(" ");
let wordScore = 0;
const alphabetScore = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26
}
let word = words[i];
let wordCount = 0;
//loop through all words in the string
for (let i = 0; i < words.length; i++) {
let word = words[i];
//loop through all characters in each word
for (let j = 0; j < word.length; j++) {
let value = alphabetScore[j];
wordCount += alphabetScore[value];
}
}
return wordCount;
}
console.log(high("man i need a taxi up to ubud"));
And this is returning an error saying
i is not defined
in let word = words[i] - how else would I define a word, then?
If it's possible to solve this Kata with my existing logic (using for-loops), please do so.
EDIT: Changed wordCount = alphabetScore.value++; to wordCount += alphabetScore[value];
EDIT 2: This is now returning NaN
EDIT 3: Latest attempt:
function myScore(input) {
let key = ["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"
];
let bestWord = "";
let bestScore = 0;
let words = input.split(" ");
for (let i = 0; i < words.length; i++) {
let score = 0;
let word = words[i];
for (let j = 0; j < word.length; j++) {
let char = word[j];
score += (key.indexOf(char) + 1);
}
if (score > bestScore) {
bestScore = score;
bestWord = word;
}
}
return bestWord;
}
ReferenceError: high is not defined
at Test.describe._
ran successfully on codewars
let key = ["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 wordScore(word) {
let score = 0;
for (let j = 0; j < word.length; j++) {
let char = word[j];
score += (key.indexOf(char) + 1);
}
return score;
}
function high(x) {
let bestWord = "";
let bestScore = 0;
words = x.split(" ");
for (let i = 0; i < words.length; i++) {
let word = words[i];
let score = wordScore(word);
if (score > bestScore) {
bestScore = score;
bestWord = word;
}
}
return bestWord;
}
console.log(high("man i need a taxi up to ubud"));
NaN means "not a number." It's usually a sign that you've tried to do arithmetic on a null or something at some point.
In this instance, your alphabetScore is a hashmap of letters - but you're looking for numeric keys let value = alphabetScore[j];: that will return undefined and undefined + 0 == NaN. Instead you need to say let value = alphabetScore[word[j]] - get the letter of the word, not the index.
To do as Jonas suggested, make two new variables where your first let word = words[i] is - and get rid of that - let highScore = 0; let highScoreWord = ""; to hold the highest one you find. Move the let wordCount = 0; inside the loop as well. Now, for each word, you'll get the word, and reset the count.
Finally, after the inner loop, say, if (wordCount > highScore) { highScore = wordCount; highScoreWord = word; }. So if it's higher than your current highest, save it; otherwise just ignore it.
Then return highScoreWord and you should be golden!
I have JS array with strings, for example:
var strArray = [ "q", "w", "w", "e", "i", "u", "r"];
I need to compare for duplicate strings inside array, and if duplicate string exists, there should be alert box pointing to that string.
I was trying to compare it with for loop, but I don't know how to write code so that array checks its own strings for duplicates, without already pre-determined string to compare.
The findDuplicates function (below) compares index of all items in array with index of first occurrence of same item. If indexes are not same returns it as duplicate.
let strArray = [ "q", "w", "w", "w", "e", "i", "u", "r"];
let findDuplicates = arr => arr.filter((item, index) => arr.indexOf(item) != index)
console.log(findDuplicates(strArray)) // All duplicates
console.log([...new Set(findDuplicates(strArray))]) // Unique duplicates
Using ES6 features
Because each value in the Set has to be unique, the value equality will be checked.
function checkIfDuplicateExists(arr) {
return new Set(arr).size !== arr.length
}
var arr = ["a", "a", "b", "c"];
var arr1 = ["a", "b", "c"];
console.log(checkIfDuplicateExists(arr)); // true
console.log(checkIfDuplicateExists(arr1)); // false
var strArray = [ "q", "w", "w", "e", "i", "u", "r", "q"];
var alreadySeen = {};
strArray.forEach(function(str) {
if (alreadySeen[str])
console.log(str);
else
alreadySeen[str] = true;
});
I added another duplicate in there from your original just to show it would find a non-consecutive duplicate.
Updated version with arrow function:
const strArray = [ "q", "w", "w", "e", "i", "u", "r", "q"];
const alreadySeen = {};
strArray.forEach(str => alreadySeen[str] ? console.log(str) : alreadySeen[str] = true);
You could take a Set and filter to the values that have already been seen.
var array = ["q", "w", "w", "e", "i", "u", "r"],
seen = array.filter((s => v => s.has(v) || !s.add(v))(new Set));
console.log(seen);
Using some function on arrays:
If any item in the array has an index number from the beginning is not equals to index number from the end, then this item exists in the array more than once.
// vanilla js
function hasDuplicates(arr) {
return arr.some( function(item) {
return arr.indexOf(item) !== arr.lastIndexOf(item);
});
}
function hasDuplicates(arr) {
var counts = [];
for (var i = 0; i <= arr.length; i++) {
if (counts[arr[i]] === undefined) {
counts[arr[i]] = 1;
} else {
return true;
}
}
return false;
}
// [...]
var arr = [1, 1, 2, 3, 4];
if (hasDuplicates(arr)) {
alert('Error: you have duplicates values !')
}
Simple Javascript (if you don't know ES6)
function hasDuplicates(arr) {
var counts = [];
for (var i = 0; i <= arr.length; i++) {
if (counts[arr[i]] === undefined) {
counts[arr[i]] = 1;
} else {
return true;
}
}
return false;
}
// [...]
var arr = [1, 1, 2, 3, 4];
if (hasDuplicates(arr)) {
alert('Error: you have duplicates values !')
}
function hasDuplicateString(strings: string[]): boolean {
const table: { [key: string]: boolean} = {}
for (let string of strings) {
if (string in table) return true;
table[string] = true;
}
return false
}
Here the in operator is generally considered to be an 0(1) time lookup, since it's a hash table lookup.
var elems = ['f', 'a','b','f', 'c','d','e','f','c'];
elems.sort();
elems.forEach(function (value, index, arr){
let first_index = arr.indexOf(value);
let last_index = arr.lastIndexOf(value);
if(first_index !== last_index){
console.log('Duplicate item in array ' + value);
}else{
console.log('unique items in array ' + value);
}
});
You have to create an empty array then check each element of the given array if the new array already has the element it will alert you.
Something like this.
var strArray = [ "q", "w", "w", "e", "i", "u", "r"];
let newArray =[];
function check(arr){
for(let elements of arr){
if(newArray.includes(elements)){
alert(elements)
}
else{
newArray.push(elements);
}
}
return newArray.sort();
}
check(strArray);
Use object keys for good performance when you work with a big array (in that case, loop for each element and loop again to check duplicate will be very slowly).
var strArray = ["q", "w", "w", "e", "i", "u", "r"];
var counting = {};
strArray.forEach(function (str) {
counting[str] = (counting[str] || 0) + 1;
});
if (Object.keys(counting).length !== strArray.length) {
console.log("Has duplicates");
var str;
for (str in counting) {
if (counting.hasOwnProperty(str)) {
if (counting[str] > 1) {
console.log(str + " appears " + counting[str] + " times");
}
}
}
}
This is the simplest solution I guess :
function diffArray(arr1, arr2) {
return arr1
.concat(arr2)
.filter(item => !arr1.includes(item) || !arr2.includes(item));
}
const isDuplicate = (str) =>{
return new Set(str.split("")).size === str.length;
}
You could use reduce:
const arr = ["q", "w", "w", "e", "i", "u", "r"]
arr.reduce((acc, cur) => {
if(acc[cur]) {
acc.duplicates.push(cur)
} else {
acc[cur] = true //anything could go here
}
}, { duplicates: [] })
Result would look like this:
{ ...Non Duplicate Values, duplicates: ["w"] }
That way you can do whatever you want with the duplicate values!
I have an array of e-mails (it can be just 1 email, or 100 emails), and I need to send the array with an ajax request (that I know how to do), but I can only send an array that has 10 or less e-mails in it. So if there is an original array of 20 e-mails I will need to split them up into 2 arrays of 10 each. or if there are 15 e-mails in the original array, then 1 array of 10, and another array of 5. I'm using jQuery, what would be the best way to do this?
Don't use jquery...use plain javascript
var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var b = a.splice(0,10);
//a is now [11,12,13,14,15];
//b is now [1,2,3,4,5,6,7,8,9,10];
You could loop this to get the behavior you want.
var a = YOUR_ARRAY;
while(a.length) {
console.log(a.splice(0,10));
}
This would give you 10 elements at a time...if you have say 15 elements, you would get 1-10, the 11-15 as you wanted.
var size = 10; var arrayOfArrays = [];
for (var i=0; i<bigarray.length; i+=size) {
arrayOfArrays.push(bigarray.slice(i,i+size));
}
console.log(arrayOfArrays);
Unlike splice(), slice() is non-destructive to the original array.
Just loop over the array, splicing it until it's all consumed.
var a = ['a','b','c','d','e','f','g']
, chunk
while (a.length > 0) {
chunk = a.splice(0,3)
console.log(chunk)
}
output
[ 'a', 'b', 'c' ]
[ 'd', 'e', 'f' ]
[ 'g' ]
You can use lodash:
https://lodash.com/docs
_.chunk(['a', 'b', 'c', 'd'], 2);
// → [['a', 'b'], ['c', 'd']]
Array.reduce could be inefficient for large arrays, especially with the mod operator. I think a cleaner (and possibly easier to read) functional solution would be this:
const chunkArray = (arr, size) =>
arr.length > size
? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
: [arr];
Assuming you don't want to destroy the original array, you can use code like this to break up the long array into smaller arrays which you can then iterate over:
var longArray = []; // assume this has 100 or more email addresses in it
var shortArrays = [], i, len;
for (i = 0, len = longArray.length; i < len; i += 10) {
shortArrays.push(longArray.slice(i, i + 10));
}
// now you can iterate over shortArrays which is an
// array of arrays where each array has 10 or fewer
// of the original email addresses in it
for (i = 0, len = shortArrays.length; i < len; i++) {
// shortArrays[i] is an array of email addresss of 10 or less
}
Another implementation:
const arr = ["H", "o", "w", " ", "t", "o", " ", "s", "p", "l", "i", "t", " ", "a", " ", "l", "o", "n", "g", " ", "a", "r", "r", "a", "y", " ", "i", "n", "t", "o", " ", "s", "m", "a", "l", "l", "e", "r", " ", "a", "r", "r", "a", "y", "s", ",", " ", "w", "i", "t", "h", " ", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t"];
const size = 3;
const res = arr.reduce((acc, curr, i) => {
if ( !(i % size) ) { // if index is 0 or can be divided by the `size`...
acc.push(arr.slice(i, i + size)); // ..push a chunk of the original array to the accumulator
}
return acc;
}, []);
// => [["H", "o", "w"], [" ", "t", "o"], [" ", "s", "p"], ["l", "i", "t"], [" ", "a", " "], ["l", "o", "n"], ["g", " ", "a"], ["r", "r", "a"], ["y", " ", "i"], ["n", "t", "o"], [" ", "s", "m"], ["a", "l", "l"], ["e", "r", " "], ["a", "r", "r"], ["a", "y", "s"], [",", " ", "w"], ["i", "t", "h"], [" ", "J", "a"], ["v", "a", "S"], ["c", "r", "i"], ["p", "t"]]
NB - This does not modify the original array.
Or, if you prefer a functional, 100% immutable (although there's really nothing bad in mutating in place like done above) and self-contained method:
function splitBy(size, list) {
return list.reduce((acc, curr, i, self) => {
if ( !(i % size) ) {
return [
...acc,
self.slice(i, i + size),
];
}
return acc;
}, []);
}
As a supplement to #jyore's answer, and in case you still want to keep the original array:
var originalArray = [1,2,3,4,5,6,7,8];
var splitArray = function (arr, size) {
var arr2 = arr.slice(0),
arrays = [];
while (arr2.length > 0) {
arrays.push(arr2.splice(0, size));
}
return arrays;
}
splitArray(originalArray, 2);
// originalArray is still = [1,2,3,4,5,6,7,8];
I would like to share my solution as well. It's a little bit more verbose but works as well.
var data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var chunksize = 4;
var chunks = [];
data.forEach((item)=>{
if(!chunks.length || chunks[chunks.length-1].length == chunksize)
chunks.push([]);
chunks[chunks.length-1].push(item);
});
console.log(chunks);
Output (formatted):
[ [ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15 ] ]
You can start with an empty array and push inside it sections with your desired range from the original array at the same time you are subtracting from your original array until is empty.
const originalArr = [1,2,3,4,5,6,7,8,9,10,11];
const splittedArray = [];
while (originalArr.length > 0) {
splittedArray.push(originalArr.splice(0,range));
}
output for range 3
splittedArray === [[1,2,3][4,5,6][7,8,9][10,11]]
output for range 4
splittedArray === [[1,2,3,4][5,6,7,8][9,10,11]]
This is also good for a fronted pagination if want.
Another method:
var longArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var size = 2;
var newArray = new Array(Math.ceil(longArray.length / size)).fill("")
.map(function() { return this.splice(0, size) }, longArray.slice());
// newArray = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];
This doesn't affect the original array as a copy, made using slice, is passed into the 'this' argument of map.
Another implementation, using Array.reduce (I think it’s the only one missing!):
const splitArray = (arr, size) =>
{
if (size === 0) {
return [];
}
return arr.reduce((split, element, index) => {
index % size === 0 ? split.push([element]) : split[Math.floor(index / size)].push(element);
return split;
}, []);
};
As many solutions above, this one’s non-destructive. Returning an empty array when the size is 0 is just a convention. If the if block is omitted you get an error, which might be what you want.
More compact:
const chunk = (xs, size) =>
xs.map((_, i) =>
(i % size === 0 ? xs.slice(i, i + size) : null)).filter(Boolean);
// Usage:
const sampleArray = new Array(33).fill(undefined).map((_, i) => i);
console.log(chunk(sampleArray, 5));
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; i < arr.length; i+= size){
newArr.push(arr.slice(i,i+size));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
Using ES6 Generators
Late to the party, but ES6 generators opened up another neat way to achieve what is asked for.
/**
* Returns chunks of size n.
* #param {Array<any>} array any array
* #param {number} n size of chunk
*/
function* chunks(array, n){
for(let i = 0; i < array.length; i += n) yield array.slice(i, i + n);
}
const result = [...chunks([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Make it work for infinite Generators
Using the same idea you can create a generator which can also generate an infinite amount of n-sized chunks from values retrieved from another (possibly infinite) generator function. This can be very handy to lazy-generate values once they are required which significantly reduces the required memory or it can even be used to generate a possibly infinite/ unknown number of chunks.
Here an example which uses two generators.
nextNaturalNumber() is an infinite generator which always returns the next natural number. I am using the ES2020 bigint datatype here so there is no restriction (by JavaScript) for the size of the value.
chunksFromIterable() creates n-sized chunks from an possibly infinite iterable.
/**
* Returns chunks of size n for a possibly infinite iterator.
* n must be >= 1
* #param {Iterable<any>} iterable any array
* #param {number} n size of chunk for n >= 1
*/
function* chunksFromIterable(iterable, n){
let arr = [];
let i = n;
for (const value of iterable) {
if(i <= 0) {
// another chunk of size n is filled => return chunk
yield arr;
arr = []; // create new empty array
i = n;
};
arr.push(value);
i--;
}
// in case the iterable is not infinite check if there are still values in the array and return them if necessary
if(arr.length > 0) yield arr;
}
/**
* Infinite iterator which always gets the next natural number.
*/
function* nextNaturalNumber(){
let i = 0n;
while(true) {
i += 1n;
yield i;
}
}
console.log("Finite iterable:");
// this version can now be used using the for ... of loop
for(const threeNaturalNumbers of chunksFromIterable([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)){
console.log(threeNaturalNumbers);
}
console.log("Infinite iterable:");
// and it can also be used for this infinite generator
for(const threeNaturalNumbers of chunksFromIterable(nextNaturalNumber(), 3)){
printBigIntArray(threeNaturalNumbers);
if(threeNaturalNumbers[0] > 30) break; // end here to avoid an infinite loop
}
// helper function to print array of bigints as this does not seem to be working for snippets
function printBigIntArray(arr){
console.log(`[${arr.join(", ")}]`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can take a look at this code . Simple and Effective .
function chunkArrayInGroups(array, unit) {
var results = [],
length = Math.ceil(array.length / unit);
for (var i = 0; i < length; i++) {
results.push(array.slice(i * unit, (i + 1) * unit));
}
return results;
}
chunkArrayInGroups(["a", "b", "c", "d"], 2);
Here is a simple one liner
var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
: (r.push([e]), r), []),
arr = Array.from({length: 31}).map((_,i) => i+1);
console.log(segment(arr,7));
as a function
var arrayChunk = function (array, chunkSize) {
var arrayOfArrays = [];
if (array.length <= chunkSize) {
arrayOfArrays.push(array);
} else {
for (var i=0; i<array.length; i+=chunkSize) {
arrayOfArrays.push(array.slice(i,i+chunkSize));
}
}
return arrayOfArrays;
}
to use
arrayChunk(originalArray, 10) //10 being the chunk size.
using recursion
let myArr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
let size = 4; //Math.sqrt(myArr.length); --> For a n x n matrix
let tempArr = [];
function createMatrix(arr, i) {
if (arr.length !== 0) {
if(i % size == 0) {
tempArr.push(arr.splice(0,size))
}
createMatrix(arr, i - 1)
}
}
createMatrix(myArr, myArr.length);
console.log(tempArr);
Note: The existing array i.e. myArr will be modified.
using prototype we can set directly to array class
Array.prototype.chunk = function(n) {
if (!this.length) {
return [];
}
return [this.slice(0, n)].concat(this.slice(n).chunk(n));
};
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].chunk(5));
let original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let size = 5;
let fragments = Array.from(Array(Math.ceil(a.length / size))).map((_,index) => a.slice(index * size,(index + 1) * size))
If you want a method that doesn't modify the existing array, try this:
let oldArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let newArray = [];
let size = 3; // Size of chunks you are after
let j = 0; // This helps us keep track of the child arrays
for (var i = 0; i < oldArray.length; i++) {
if (i % size === 0) {
j++
}
if(!newArray[j]) newArray[j] = [];
newArray[j].push(oldArray[i])
}
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; arr.length>size; i++){
newArr.push(arr.splice(0,size));
}
newArr.push(arr.slice(0));
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
You can use the following code to achieve the required functionality
const splitter = (arr, splitBy, cache = []) => {
const tmp = [...arr]
while (tmp.length) cache.push(tmp.splice(0, splitBy))
return cache
}
const split = splitter([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], 10)
console.log(split);
Notice, it was an array of length 22 then the splitter function splits it into 2 smaller arrays of 10 items and 1 array of 2 items.
let a = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11];
let _splitCount = 3;
let b = a.reduce((acc, curr, index) => {
if (index % _splitCount === 0) {
acc.push([curr]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, []);
this is the easy solution i think❤️
You can use the below function if you know the number array (numGroups) to be split.
function createGroups(arr, numGroups) {
const perGroup = Math.ceil(arr.length / numGroups);
return new Array(numGroups)
.fill('')
.map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup));
}
Sample Use:
createGroups([0, 1, 2, 3, 4, 5, 6], 3); //arr = [0, 1, 2, 3, 4, 5, 6] and numGroups = 3