Related
I came across a problem in an online course:
Write a function called vowelCount which accepts a string and returns an object with the keys as the vowel and the values as the number of times the vowel appears in the string. This function should be case insensitive so a lowercase letter and uppercase letter should count
Examples:
vowelCount('Elie') // {e:2,i:1};
the solution from the instructor came like this:
function vowelCount(str){
var splitArr = str.toLowerCase().split("");
var obj = {};
var vowels = "aeiou";
splitArr.forEach(function(letter){
if(vowels.indexOf(letter) !== -1){
if(obj[letter]){
obj[letter]++;
} else{
obj[letter] = 1;
}
}
});
return obj;
}
I understand the solution until the second "if" statement. I know that the first "if" statement is to check if the "letters" in the input string belongs to the "vowels". Then in the second "if" it is checking if the "letter is in the empty "obj" object created above, but at that line, the "obj" is empty bofore the "letter" is added to it, so what is the point for that "if". Also, why does adding this new "letter" to the object require an increment. I tried the code without increment and the object is still empty.
It's checking if you've ever seen the letter before in the loop. If you've never written to obj[letter], then when you do obj[letter], you get back the value undefined, which is falsy (treated as false by things like an if). if(obj[letter]) is checking for a truthy value (a value that isn't falsy) so that it adds to the number already stored at obj[letter] if it's there (obj[letter]++). But when it sees a falsy value like undefined, it takes the else branch and sets obj[letter] to 1 because the code knows that letter hasn't been seen before.
Just FWIW, while still entirely valid, that's fairly old-style JavaScript code (circa the ES5 standard, 2009). ES2015 added several features you'd use to solve this problem today:
function vowelCount(str){
// Use a Map to remember how many of each ltter you've
// seen. You could use an object as well, but ideally you'd
// create the object with out a prototype to avoid having
// any conflict with inherited properties from `Object.prototype`.
const counts = new Map(); // const counts = Object.create(null);
// The set of vowels
const vowels = new Set("aeiou");
// Loop through the letters
for (const letter of str) {
// If it's not a vowel...
if (!vowels.has(letter)){
// Count it
const currentCount = counts.get(letter) || 0;
counts.set(letter, currentCount + 1);
// Or using an object:
// const currentCount = counts[letter] || 0;
// counts[letter] = currentCount + 1;
}
});
return counts;
}
We can use regular expression to match vowels in a sentence.
Regular expression to match all occurrence of vowel in a string:/[aeiouAEIOU]+?/g
Below is the working code snippet:
//function that takes string as input
//function returns an object containing vowel count without case in account.
function vowelCount(input) {
//to get vowel count using string.match
var arrVowels =input.match(/[aeiouAEIOU]+?/g);
//acc=accumulator, curr=current value
return arrVowels.reduce(function (acc, curr) {
if (typeof acc[curr.toLowerCase()] == 'undefined') {
acc[curr.toLowerCase()] = 1;
}
else {
acc[curr.toLowerCase()] += 1;
}
return acc;
// the blank object below is default value of the acc (accumulator)
}, {});
}
Forgive me if this is to basic or have been asked already, but I'm stuck at this problem and I feel like it must be something simple I'm not seeing.
I want to add all numbers in the array using recursion(done!) and I'm missing a statement to ignore all other types of values.
For example:
var arr = [1,'a','b',2,[1],'c']
sumValues(arr) // => 4 .
function sumValues(arr){
if(arr.length === 0){
return 0;
} // if the array is empty
if(arr.length > 0){
if(Array.isArray(arr[0])){
return sumValues(arr[0]);
} // if the next element is an array
return arr.shift() + sumValues(arr);
}
}
You can use Number.isFinite(value) to determine whether a variable is a number other than NaN or Infinity.
Based on this test, check and conditionally add values to the summation.
function sumValues(arr){
if (arr.length === 0){
return 0;
} // if the array is empty
if (arr.length > 0) {
if (Array.isArray(arr[0])){
return sumValues(arr[0]);
} // if the next element is an array
// pop the first element off the array
var value = arr.shift();
// check its type and conditionally add it to the summation
return (Number.isFinite(value) ? value : 0) + sumValues(arr);
}
}
var arr = [1,'a','b',2,[1],'c']
console.log(sumValues(arr)); // 4
arr = [1,'3','b',2,[1,[4,3]],'c'];
console.log(sumValues(arr)); // 11 (because '3' is ignored)
You can use isNaN() to test if something is Not a Number.
By using the boolean inversion operator ! you can test if it's a number:
!isNaN(variable)
Basically it says: if variable is Not Not A Number
A double negative is a positive, so it becomes: if variable Is a Number
function sumValues(arr){
if(Array.isArray(arr)) {
if(arr.length > 0) {
// Get the first value and remove it from the array.
var first = arr.shift();
// Test if it's numeric.
if(!isNaN(first)) {
// If it is, parse the value, add the rest resulting array values.
return parseInt(first) + parseInt(sumValues(arr));
}
// if the first item is an array, we need to iterate that one too.
if(Array.isArray(first)) {
return parseInt(sumValues(first)) + parseInt(sumValues(arr));
}
// It isn't a number, just continue with what's left of the array.
return parseInt(sumValues(arr));
}
// The array is empty, return 0.
return 0;
}
// It isn't an array
else {
// Is it an number?
if(!isNaN(arr)) {
// return the number
return parseInt(arr);
}
// return 0, it's a dead end.
return 0;
}
}
var arr = [1,'a','b',2,[1],'c'];
console.log(sumValues(arr)) // => 4 .
arr = [1,'3','b',2,[1,[4,3]],'c'];
console.log(sumValues(arr)) // => 14 .
arr = 'foobar';
console.log(sumValues(arr)) // => 0 .
arr = '10';
console.log(sumValues(arr)) // => 10 .
arr = ['foobar',10,'20',[10,20,'foobar']];
console.log(sumValues(arr)) // => 60 .
You're squashing an array, there is no need to create your own reduction function. Use Array#reduce to squash the array. Upon each iteration, check if the current element is a number; if it is, add it to the accumulator; if not, move on.
To make it recursive (as shown in the example below) check if the current element is an array; if it is, add the result of calling the function on the element to the accumulator.
const input = [1,'a','b',2,[1],'c'];
const sumValues = i =>
i.reduce((m, e) => m + (e instanceof Array ? sumValues(e) : (isNaN(e) ? 0 : e)), 0);
console.log(sumValues(input));
My understanding is that the contents of a while loop executes while the condition is true. While working off of an example from an awesome O'Riely book, I've come across this implementation of the while loop...
window.onload = function(){
var next, previous, rewind; // globals
(function(){
// Set private variables
var index = -1;
var data = ['eeny', 'meeny', 'miney', 'moe'];
var count = data.length;
next = function(){
if (index < count) {
index ++;
};
return data[index];
};
previous = function(){
if (index <= count){
index --;
}
return data[index];
};
rewind = function(){
index = -1;
};
})();
// console.log results of while loop calling next()...
var a;
rewind();
while(a = next()){
// do something here
console.log(a);
}
}
I guess I'm wondering why, in this code, the while loop is not resolving to true infinitely? The function, next() isn't returning false after var index stops incrementing (++), is it? Shouldn't the console just be outputting eeny, meeny, miney, moe, moe, moe, moe.....etc...
I know this has probably been asked in some form, but have done searching and can't find a question or answer that explains using while (a = function()) {// do something} and how this loop is stopping after one pass through the array.
About why while (a = next()) {/*do something*/} doesn't repeat infinitely, it's about being coerced to false that counts - arguments are converted to booleans before being tested by the while loop. Things that coerce to false include 0, -0, undefined, null, "", NaN, and of course false itself.
When you assign something, it returns the value of the assignment itself. For example, if you do something like this:
var a;
console.log(a = '1234567890abcdefghijklmnopqrstuvwxyz');
It will log 1234567890abcdefghijklmnopqrstuvwxyz.
When the next performs index++, this increments the counter for the element index in the data array. This means that it will look for the next element in the data array every time you run the next() function - if there are no more elements, it will return undefined and therefore end the loop.
For example, see this:
var index = 0;
data = ['a','b','c'];
data[index]; // 'a'
index++;
data[index]; // 'b'
index++;
data[index]; // 'c'
index++;
data[index]; // undefined - if passed this will coerce to false and end the loop
Boolean(data[index]); // false
if (index < count) {
index ++;
};
When index is count - 1, this will still change index to count, right? And count is data.length. So, it then does this:
return data[index];
Which becomes
return data[data.length];
Since the length of an array is out of bounds of the array (they are zero-based), it will give undefined.
while(a = next()){
will become
while(a = undefined){
Since undefined is a falsy value, the loop will not be entered.
No,
It is not going to be an infinite loop. The while loop is basically going through the array and outputting it and when it is at the end of the array it just returns false and quits the loop.
This is something like;
foreach(a as nextArray)
{
//output
}
Hope this helps.
Possible Duplicate:
Easiest way to find duplicate values in a javascript array
How do I check if an array has duplicate values?
If some elements in the array are the same, then return true. Otherwise, return false.
['hello','goodbye','hey'] //return false because no duplicates exist
['hello','goodbye','hello'] // return true because duplicates exist
Notice I don't care about finding the duplication, only want Boolean result whether arrays contains duplications.
If you have an ES2015 environment (as of this writing: io.js, IE11, Chrome, Firefox, WebKit nightly), then the following will work, and will be fast (viz. O(n)):
function hasDuplicates(array) {
return (new Set(array)).size !== array.length;
}
If you only need string values in the array, the following will work:
function hasDuplicates(array) {
var valuesSoFar = Object.create(null);
for (var i = 0; i < array.length; ++i) {
var value = array[i];
if (value in valuesSoFar) {
return true;
}
valuesSoFar[value] = true;
}
return false;
}
We use a "hash table" valuesSoFar whose keys are the values we've seen in the array so far. We do a lookup using in to see if that value has been spotted already; if so, we bail out of the loop and return true.
If you need a function that works for more than just string values, the following will work, but isn't as performant; it's O(n2) instead of O(n).
function hasDuplicates(array) {
var valuesSoFar = [];
for (var i = 0; i < array.length; ++i) {
var value = array[i];
if (valuesSoFar.indexOf(value) !== -1) {
return true;
}
valuesSoFar.push(value);
}
return false;
}
The difference is simply that we use an array instead of a hash table for valuesSoFar, since JavaScript "hash tables" (i.e. objects) only have string keys. This means we lose the O(1) lookup time of in, instead getting an O(n) lookup time of indexOf.
You could use SET to remove duplicates and compare, If you copy the array into a set it will remove any duplicates. Then simply compare the length of the array to the size of the set.
function hasDuplicates(a) {
const noDups = new Set(a);
return a.length !== noDups.size;
}
One line solutions with ES6
const arr1 = ['hello','goodbye','hey']
const arr2 = ['hello','goodbye','hello']
const hasDuplicates = (arr) => arr.length !== new Set(arr).size;
console.log(hasDuplicates(arr1)) //return false because no duplicates exist
console.log(hasDuplicates(arr2)) //return true because duplicates exist
const s1 = ['hello','goodbye','hey'].some((e, i, arr) => arr.indexOf(e) !== i)
const s2 = ['hello','goodbye','hello'].some((e, i, arr) => arr.indexOf(e) !== i);
console.log(s1) //return false because no duplicates exist
console.log(s2) //return true because duplicates exist
Another approach (also for object/array elements within the array1) could be2:
function chkDuplicates(arr,justCheck){
var len = arr.length, tmp = {}, arrtmp = arr.slice(), dupes = [];
arrtmp.sort();
while(len--){
var val = arrtmp[len];
if (/nul|nan|infini/i.test(String(val))){
val = String(val);
}
if (tmp[JSON.stringify(val)]){
if (justCheck) {return true;}
dupes.push(val);
}
tmp[JSON.stringify(val)] = true;
}
return justCheck ? false : dupes.length ? dupes : null;
}
//usages
chkDuplicates([1,2,3,4,5],true); //=> false
chkDuplicates([1,2,3,4,5,9,10,5,1,2],true); //=> true
chkDuplicates([{a:1,b:2},1,2,3,4,{a:1,b:2},[1,2,3]],true); //=> true
chkDuplicates([null,1,2,3,4,{a:1,b:2},NaN],true); //=> false
chkDuplicates([1,2,3,4,5,1,2]); //=> [1,2]
chkDuplicates([1,2,3,4,5]); //=> null
See also...
1 needs a browser that supports JSON, or a JSON library if not.
2 edit: function can now be used for simple check or to return an array of duplicate values
You can take benefit of indexOf and lastIndexOf. if both indexes are not same, you have duplicate.
function containsDuplicates(a) {
for (let i = 0; i < a.length; i++) {
if (a.indexOf(a[i]) !== a.lastIndexOf(a[i])) {
return true
}
}
return false
}
If you are dealing with simple values, you can use array.some() and indexOf()
for example let's say vals is ["b", "a", "a", "c"]
const allUnique = !vals.some((v, i) => vals.indexOf(v) < i);
some() will return true if any expression returns true. Here we'll iterate values (from the index 0) and call the indexOf() that will return the index of the first occurrence of given item (or -1 if not in the array). If its id is smaller that the current one, there must be at least one same value before it. thus iteration 3 will return true as "a" (at index 2) is first found at index 1.
is just simple, you can use the Array.prototype.every function
function isUnique(arr) {
const isAllUniqueItems = input.every((value, index, arr) => {
return arr.indexOf(value) === index; //check if any duplicate value is in other index
});
return isAllUniqueItems;
}
One nice thing about solutions that use Set is O(1) performance on looking up existing items in a list, rather than having to loop back over it.
One nice thing about solutions that use Some is short-circuiting when the duplicate is found early, so you don't have to continue evaluating the rest of the array when the condition is already met.
One solution that combines both is to incrementally build a set, early terminate if the current element exists in the set, otherwise add it and move on to the next element.
const hasDuplicates = (arr) => {
let set = new Set()
return arr.some(el => {
if (set.has(el)) return true
set.add(el)
})
}
hasDuplicates(["a","b","b"]) // true
hasDuplicates(["a","b","c"]) // false
According to JSBench.me, should preform pretty well for the varried use cases. The set size approach is fastest with no dupes, and checking some + indexOf is fatest with a very early dupe, but this solution performs well in both scenarios, making it a good all-around implementation.
function hasAllUniqueChars( s ){
for(let c=0; c<s.length; c++){
for(let d=c+1; d<s.length; d++){
if((s[c]==s[d])){
return false;
}
}
}
return true;
}
I have n arrays with which I need to determine if x is in all of the n arrays. (where n is any number, and x is a numeric value) I have something like the following in place, but it's always ending up false.
function filterArrays()
{
var x = $(this).attr('id'); // ex: 2
var arrays = [[1,2,3],[2,4,6]];
var result = false;
for each (var n in arrays)
{
result = result ^ (n.indexOf(x) > -1);
}
}
How do I make result equal to true when x is in both arrays, but when x is not in both arrays, make result equal to false?
The function above will be used with jQuery's filter() method. Example:
$(arrayOfElementsWithNumericIds).filter(arrayFilter);
// arrayOfElementsWithNumericIds prototype: [div#1,div#2,div#3,...]
I'm thinking that a bitwise operation is called for, but I could be wrong. Please explain why your solution is right and why mine isn't working. (for bonus points)
Here are some issues with your example:
Comparing number to string (id is a string). Use x = parseInt(...)
Using the ^ operator. Instead initialize result to true and use &&.
Get rid of each. The correct syntax is for (key in object)
I've modified your code as little as possible:
function filterArrays()
{
var x = parseInt($(this).attr('id')); // ex: 2
var arrays = [[1,2,3],[2,4,6]];
var result = true;
for (var n in arrays)
{
result = result && (arrays[n].indexOf(x) > -1);
}
return result;
}
That being said, you can really optimize your code by using Array.every() and Array.some(). Also, using $(this).attr('id') creates a jQuery object unnecessarily since you can just say this.id directly.
function filterArrays()
{
var x = parseInt(this.id); // ex: 2
var arrays = [[1,2,3],[2,4,6]];
var result = arrays.every(function(array)
{
return array.some(function(item)
{
return item === x;
});
});
return result;
}
I think that you are looking for this:
var result = true;
for each (var n in arrays)
{
result = result && (n.indexOf(x) > -1);
}
That is, assume that the value is in all the arrays to start. Then using the AND (&&) operator you get
true AND (value is in current array)
if at any time the value is not in an array it becomes false and the entire operation will be false. Otherwise it remains true until the end of the loop.
xor's not the way to go. Look at it this way:
search for 2, start result = false
1st array: 2 is present, result = false xor true = true
2nd array: 2 is present, result = true xor true = false
end: result is false (WRONG)
search for 4, start result = false
1st array: 4 is present, result = false xor true = true
2nd array: 4 is absent, result = true xor false = true
end: result is true (WRONG)
You want a cummulative bit-wise and.
start: result = true, search for 2
1st array: 2 is present, result = true and true = true
2nd array: 2 is present, result = true and true = true
end: result is true (RIGHT)
start: result = true, search for 4
1st array: 4 is present, result = true and true = true
2nd array: 4 is absent, result = true and false = false
end: result if false (RIGHT)
Why Don't you extend the array prototype with a contains method? that way you can loop over each array and or/and the current result with the previous one.
You can loop over your 'arrays' object if you want, but, I think you just want a Set Intersect operation. This is one way to do it in jQuery, and it won't care about if the attr(id) value of x is an integer or string. I'm on lunch, i'll test this in a page quick...
function filterArrays(){
var x = $(this).attr("id");
var arrays = [[1,2,3],[2,4,6]];
var result = ($.inArray(arrays[0], x )>0 && $.inArray(arrays[1], x) >0);
return result;
}
Using http://phrogz.net/JS/ArraySetMath.js you could:
var sets = [[1,2,3],[2,3,7],[1,7,2]];
var isect = sets[0];
for (var i=1,len=sets.length;i<len;++i){
isect = isect.intersection( sets[i] );
}
console.log( isect );
// [2]
Or, using JS.Set you could:
var sets = [[1,2,3],[2,3,7],[1,7,2]];
// Or JS.HashSet or JS.Set
var isect = new JS.SortedSet(sets[0]);
for (var i=1,len=sets.length;i<len;++i){
isect = isect.intersection( new JS.SortedSet(sets[i]) );
}