Related
I have has like this
var hash = {};
hash['a'] = ["a", "b", "c", "d", "e"];
hash['b'] = ["a", "b", "c"];
hash['k'] = ["q", "b"];
Math.max(Object.keys(hash)
.map(function(key) {
return hash[key].length;
})
)
It returns 5 but, I want to get the hash key 'a' at the same time.
Is it possible??
Combine Object.entries() and Array.prototype.reduce() to get the key and the elements behind that key (and their length)
var hash = {
a: ["a", "b", "c", "d", "e"]
,b: ["a", "b", "c"]
,k: ["q", "b"]
};
var result = Object.entries(hash)
.reduce((r, c) => r[1].length > c[1].length ? r : c);
console.log(result);
Does this fulfil your requirement?
I have used sort instead of Math.max here to find the maxima
var hash = {};
hash['a'] = ["a", "b", "c", "d", "e"];
hash['b'] = ["a", "b", "c"];
hash['k'] = ["q", "b"];
var result = Object.keys(hash)
.map(function(key) {
return {[key]: hash[key].length};
}).sort(function(a, b){
return a.key > b.key
})[0]
console.log(result);
i have used this approach for your problem:
var hash = {};
hash['a'] = ["a", "b", "c", "d", "e"];
hash['b'] = ["a", "b", "c"];
hash['k'] = ["q", "b"];
var largestInfo = {
key:'',
length:0
}
Object.keys(hash).map(function(key) {
if(hash[key].length > largestInfo.length){
largestInfo.key = key;
largestInfo.length = hash[key].length;
}
return hash[key].length;
});
console.log(largestInfo);
I know there have been a few posts about Caesar Ciphers in the past, which I have had a look at, but I haven't found an answer which has helped me to solve this kata, hence my post.
The language is JavaScript. I've written 3 tests, 2 of which are passing so far, but the third is not. I tried to use a nested for loop to loop over the alphabet and the str, and compare them, then shift the alphabet index up/down according to whatever the number was, then push that letter into a new array, and return the joined array at the end.
It's working for positive numbers, but not negatives. (I should also point out that I haven't thought of how to handle spaces yet, I just wanted to get it working for single words first, then take it from there, thanks!)
Any pointers in the right direction would be appreciated.
Kata Instructions:
The function caesarCipher should take a string and a number (n) and return a new string with a Caesar cipher applied. A Caesar cipher replaces each plaintext letter with a different one a fixed number of places up or down the alphabet. N represents the number of shifts up or down the alphabet should be applied. It may be negative or positive.
E.g.
caesarCipher('hello', 2)
--> 'jgnnq'
caesarCipher('hello world!', -3)
--> 'ebiil tloia!'
My tests:
const caesarCipher = require("../katas/caesar-cipher");
const { expect } = require("chai");
describe.only("caesarCipher", () => {
it("returns an empty string when passed an empty string", () => {
const alphabet = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z"
];
const str = "";
const num = 2;
const actualResults = caesarCipher(alphabet, str, num);
const expectedResults = "";
expect(actualResults).to.equal(expectedResults);
});
it("returns a string with the letters replaced by the number of shifts up the alphabet", () => {
const alphabet = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z"
];
const str = "hi";
const num = 2;
const actualResults = caesarCipher(alphabet, str, num);
const expectedResults = "jk";
expect(actualResults).to.equal(expectedResults);
});
it("returns a string with the letters replaced by the number of shifts down the alphabet", () => {
const alphabet = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z"
];
const str = "dog";
const num = -3;
const actualResults = caesarCipher(alphabet, str, num);
const expectedResults = "ald";
expect(actualResults).to.equal(expectedResults);
});
});
My Solution:
function caesarCipher(alphabet, str, num) {
const strToArray = str.split("");
console.log(strToArray);
const cipheredStr = [];
for (let i = 0; i < strToArray.length; i++) {
for (let j = 0; j < alphabet.length; j++) {
if (strToArray[i] === alphabet[j] && Math.sign(num) === 1) {
console.log(Math.sign(num));
cipheredStr.push(alphabet[(j += num)]);
} else if (strToArray[i] === alphabet[j] && Math.sign(num) === -1) {
console.log(Math.sign(num));
console.log(alphabet[(j -= num)]);
cipheredStr.push(alphabet[(j -= num)]);
}
}
}
console.log(cipheredStr.join(""));
return cipheredStr.join("");
}
The Results:
caesarCipher
[]
✓ returns an empty string when passed an empty string
[ 'h', 'i' ]
1
1
jk
✓ returns a string with the letters replaced by the number of shifts up the alphabet
[ 'd', 'o', 'g' ]
-1
g
-1
r
-1
j
jum
1) returns a string with the letters replaced by the number of shifts down the alphabet
2 passing (15ms)
1 failing
1) caesarCipher
returns a string with the letters replaced by the number of shifts down the alphabet:
AssertionError: expected 'jum' to equal 'ald'
+ expected - actual
-jum
+ald
at Context.<anonymous> (spec/caesar-cipher.spec.js:108:30)
at processImmediate (internal/timers.js:456:21)
The problem is that when num is negative, you are doing the mapping:
cipheredStr.push(alphabet[(j -= num)]);
But num is negative. When you subtract a negative number what you are doing is adding its absolute value.
Instead, you should do:
cipheredStr.push(alphabet[j + num]);
Also, note that to compute the index in the alphabet you don't need to put the = in there.
Side notes
I understand that your solution is a work in progress. You have to take into consideration:
What happens when you sum j + num to do the translation and it goes out of boundaries of your alphabet. Same thing could happen with a negative value of num.
In the declaration of the problem, it states that caesarCipher should only accept two parameters, but you are passing the alphabet as first parameter!
Good luck with your code and keep trying :)
let alphabets = 'abcdefghijklmnopqrstuvwxyz';
let arr = alphabets.split('')
// Cipher Get
function getCipher(str= alphabets, shift=3){
return arr.reduce((a, c, i) => {
let result = [...a]
let tIndex = ( i + shift) % arr.length
result[i]=arr[tIndex]
return result;
},[])
}
// Encrypt
let stringToEnc = 'danger'
console.log('Plain Text -', stringToEnc)
let cipheredAlphabets = getCipher()
let encryptedStr = stringToEnc
.toLowerCase()
.split('')
.map(function(p, i){
let indexInAlphabets = arr.findIndex(c => p == c)
return (cipheredAlphabets[indexInAlphabets])
})
let encryptedText = encryptedStr.join('')
console.log('encrypted text - ', encryptedText)
// Decrypt
let cipherForDecrypt = getCipher(alphabets, -3)
let decryptedStr = encryptedText
.toLowerCase()
.split('')
.map(function(p, i){
let indexInAlphabets = cipheredAlphabets.findIndex(c => p == c)
return (arr[indexInAlphabets])
})
console.log('decrypted text - ', decryptedStr.join(''))
let's assume that we have two arrays like these:
let s1 = ["A", "B", "C", "D", "B"] // Note: "B" has been occurred 2 times
let s2 = ["A", "B", "C", "D", "B", "A", "X", "Y"] // // Note: "B" has been occurred 2 times just like s1 but we have another occurrence for "A"
I want to create a new array (let s3) based on these two arrays with this rule that we'll remove the element(s) with occurrences more than the same element's occurrence in array s1
so the s3 array would be like this:
let s3 = ["A", "B", "C", "D", "B", "X", "Y"] // we just removed "A" cause it occurred more than "A" occurances in s1
Note that we want anything else ("X", "Y"). we just want to handle the extra occurrences.
so far I can find the index of repeated occurrences like this but I can't find a way to compare each occurrence to see if it is an extra one or not (complicated):
let s1 = ["A", "B", "C", "D", "B"]
let s2 = ["A", "B", "C", "D", "B", "A", "X", "Y"]
var s3 = [];
for (let i = 0; i < s2.length; i++) {
for (let j = 0; j < s1.length; j++) {
if (s2[i] === s1[j]) {
s3.push(i);
break;
}
}
}
console.log(s3)
My English is poor and I don't know if I could explain the issue or not!
Note: I can simply use s3 = [...new Set(s2)] to remove repeated elements but I want something else.
Since you want to keep everything originally in s1 but only add items from s2 if they aren't already in you can initially set s3 to s1. Then loop over s2 and if s1 does not have the value then push it into s3.
const s1 = ["A", "B", "C", "D", "B"];
const s2 = ["A", "B", "C", "D", "B", "A", "X", "Y"];
const s3 = [...s1];
s2.forEach(d => {
if (!s1.includes(d)) s3.push(d);
});
console.log(s3);
You can use Map and filter
First Map the array1 to [key, value] format
Iterate over second array, if it's found in Map and value is grater than zero return true else return false,
If it is not found return true
let s1 = ["A", "B", "C", "D", "B"]
let s2 = ["A", "B", "C", "D", "B", "A", "X", "Y"]
let map = new Map()
s1.forEach(v=>{
if(map.has(v)){
map.set(v,map.get(v)+1)
} else{
map.set(v,1)
}
})
let final = s2.filter(val=>{
if(map.has(val)){
if(map.get(val)){
map.set(val, map.get(val)-1)
return true
}
return false
}
return true
})
console.log(final)
You could count the values of the first set and return the values for a given count or if not known then all items.
var s1 = ["A", "B", "C", "D", "B"],
s2 = ["A", "B", "C", "D", "B", "A", "X", "Y"],
count = s1.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map),
s3 = s2.filter(v => count.get(v) ? count.set(v, count.get(v) - 1) : !count.has(v));
console.log(...s3);
I have an array like var aa = ["a","b","c","d","e","f","g","h","i","j","k","l"]; I wanted to remove element which is place on even index. so ouput will be line aa = ["a","c","e","g","i","k"];
I tried in this way
for (var i = 0; aa.length; i = i++) {
if(i%2 == 0){
aa.splice(i,0);
}
};
But it is not working.
Use Array#filter method
var aa = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"];
var res = aa.filter(function(v, i) {
// check the index is odd
return i % 2 == 0;
});
console.log(res);
If you want to update existing array then do it like.
var aa = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
// variable for storing delete count
dCount = 0,
// store array length
len = aa.length;
for (var i = 0; i < len; i++) {
// check index is odd
if (i % 2 == 1) {
// remove element based on actual array position
// with use of delete count
aa.splice(i - dCount, 1);
// increment delete count
// you combine the 2 lines as `aa.splice(i - dCount++, 1);`
dCount++;
}
}
console.log(aa);
Another way to iterate for loop in reverse order( from last element to first ).
var aa = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"];
// iterate from last element to first
for (var i = aa.length - 1; i >= 0; i--) {
// remove element if index is odd
if (i % 2 == 1)
aa.splice(i, 1);
}
console.log(aa);
you can remove all the alternate indexes by doing this
var aa = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"];
for (var i = 0; i < aa.length; i++) {
aa.splice(i + 1, 1);
}
console.log(aa);
or if you want to store in a different array you can do like this.
var aa = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"];
var x = [];
for (var i = 0; i < aa.length; i = i + 2) {
x.push(aa[i]);
}
console.log(x);
You can use .filter()
aa = aa.filter((value, index) => !(index%2));
You can use temporary variable like below.
var a = [1,2,3,4,5,6,7,8,9,334,234,234,234,6545,7,567,8]
var temp = [];
for(var i = 0; i<a.length; i++)
if(i % 2 == 1)
temp.push(a[i]);
a = temp;
in Ecmascript 6,
var aa = ["a","b","c","d","e","f","g","h","i","j","k","l"];
var bb = aa.filter((item,index,arr)=>(arr.splice(index,1)));
console.log(bb);
const aa = ["a","b","c","d","e","f","g","h","i","j","k","l"];
let bb = aa.filter((items, idx) => idx % 2 !== 0)
I read here that splice has O(N) time complexity. Don't use it in a loop!
A simple alternative for removing odd indexes in place:
for (let idx = 0; idx < aa.length; idx += 2)
aa[idx >> 1] = aa[idx];
aa.length = (aa.length + 1) >> 1;
I use x >> 1 as a shortcut to Math.floor(x/2).
Hi everyone Can't figure out how can I take element from array in a random way and without repeating. Here is my code:
var letters = [ "A", "A", "B", "B", "C", "C", "D", "D", "E", "E",
"F", "F", "G", "G", "H", "H", "I", "I", "J", "J" ],
cards = document.getElementsByClassName( "cards" ),
cardBoxes = document.getElementsByClassName( "card-boxes" );
//generate random number
function randomNum( nums ) {
return Math.floor( Math.random() * nums.length );
}
//hide letter behind card in random way
function hideLetter() {
for ( var i = cards.length - 1; i >= 0; i-- ) {
var randomLetter = letters[ randomNum(letters) ];
cards[i].textContent = randomLetter;
};
}
hideLetter();
I take element in a random way, but Math.random repeating themselves. I think I have to write some sort of if statment, which will be detect if element was taken two times, but cant figure how to do it. Looking for advice. Thanks.
Here Codepen of problem http://codepen.io/Kuzyo/pen/vdlai
The sure way is to remove element from the array after it was used. This way it will never be repeated
randomise your array, then walk through the randomised array. This has the benefit of the usual "remove elements while using random indices", while keeping the sequence around in case you need a repeat operation that relies on the same random sorting:
function randomize(array) {
var copy = array.slice(),
random = [],
element, pos;
while(copy.length>0) {
pos = (Math.random()*copy.length) | 0; // bit operation forces 32-bit int
subarray = copy.splice(pos, 1);
random.push(subarray[0]);
}
return random;
}
var letters = [ "A", "A", "B", "B", "C", "C", "D", "D", "E", "E",
"F", "F", "G", "G", "H", "H", "I", "I", "J", "J" ],
randomLetters = randomize(letters);
randomLetters.forEach(function(letter) {
// create a card for this letter
});
// do we need it again, in a new random fashion?
// then we can simply call this:
randomLetters = randomize(letters);
Here is my version, with a generator. The function returned by makeRandomGenerator will return random, non-repeating members of inputArray. Once all elements have been used, it will return undefined.
function shuffle(array) {
return array.sort(function() {
return Math.random() > 0.5 ? 1 : -1;
});
}
function makeRandomGenerator(inputArr) {
var arr = inputArr.slice(0);
shuffle(arr);
return function() {
return arr.pop();
}
}
to use:
var letterGenerator = makeRandomGenerator(letters);
function hideLetter() {
for ( var i = cards.length - 1; i >= 0; i-- ) {
var randomLetter = letterGenerator();
if (randomLetter === undefined) {
return;
}
cards[i].textContent = randomLetter;
};
}
hideLetter();
Randomize the array and shift off values from the array :
var letters = [ "A", "A", "B", "B", "C", "C", "D", "D", "E", "E", "F", "F", "G", "G", "H", "H", "I", "I", "J", "J" ],
cards = document.getElementsByClassName( "cards" ),
i = letters.length, j, temp;
while ( --i ) {
j = Math.floor( Math.random() * (i - 1) );
temp = letters[i];
letters[i] = letters[j];
letters[j] = temp;
}
for ( var i = cards.length; i--; ) {
cards[i].innerHTML = letters.shift();
}
FIDDLE