Understanding Hashmaps with Javascript - javascript

I need help understanding what and how to use hash maps in javascript. I have an example where
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Can someone breakdown what this hashmap solution is doing and why it's better? Also if someone would be kind of enough to give me a similar problem to practice with that would extremely helpful.
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
My BruteForce Solution
for (var i = 0; i < nums.length; i++) {
for (var j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
result.push(i);
result.push(j);
}
}
}
return result;
}
console.log(twoSum([2, 7, 11, 15], 9));
HashMapSolution
function twoSumBest(array, target) {
const numsMap = new Map();
for (let i = 0; i < array.length; i++) {
if(numsMap.has(target - array[i])) {
return [numsMap.get(target - array[i], i)];
// get() returns a specified element associated with the specified key from the Map object.
} else {
numsMap.set(array[i], i);
// set() adds or updates an element with a specified key and value to a Map object.
}
}
}

If you want to reach 10 and you have 7, you already know that the other number that is needed is 3. So for every number in the array, you only have to check wether the complementary number is in the array, you don't necessarily have to search for it.
If we go over all array entries once (let's call them a) and add them to a hashmap with their index, we can go over the array again and for each entry (b), we can check if the hashmap contains an a (where a + b = target). If that entry is found, the index of b is known, and the index of a can be retrieved from the hashmap¹. Now using that method we only iterate the array twice (or just once, doesn't matter that much), so if you have 1000 numbers, it'll iterate 1000 times. Your solution will iterate 1000 * 1000 times (worst case). So the hashtable approach is way faster (for larger arrays).
¹ Hashmaps are very special, as the time it takes to look up a key takes a constant amount of time, so it does not matter wether the array has 10 or 10 million entries (the time complexity is constant = O(1)). Thus, looking up in an hashtable is way better than searching inside of an array (which takes more time with more entries = O(n)).

Related

Why does an object exist in two sum solution? [duplicate]

Im just wondering who can explain the algorithm of this solution step by step. I dont know how hashmap works. Can you also give a basic examples using a hashmap for me to understand this algorithm. Thank you!
var twoSum = function(nums, target) {
let hash = {};
for(let i = 0; i < nums.length; i++) {
const n = nums[i];
if(hash[target - n] !== undefined) {
return [hash[target - n], i];
}
hash[n] = i;
}
return [];
}
Your code takes an array of numbers and a target number/sum. It then returns the indexes in the array for two numbers which add up to the target number/sum.
Consider an array of numbers such as [1, 2, 3] and a target of 5. Your task is to find the two numbers in this array which add to 5. One way you can approach this problem is by looping over each number in your array and asking yourself "Is there a number (which I have already seen in my array) which I can add to the current number to get my target sum?".
Well, if we loop over the example array of [1, 2, 3] we first start at index 0 with the number 1. Currently, there are no numbers which we have already seen that we can add with 1 to get our target of 5 as we haven't looped over any numbers yet.
So, so far, we have met the number 1, which was at index 0. This is stored in the hashmap (ie object) as {'1': 0}. Where the key is the number and the value (0) is the index it was seen at. The purpose of the object is to store the numbers we have seen and the indexes they appear at.
Next, the loop continues to index 1, with the current number being 2. We can now ask ourselves the question: Is there a number which I have already seen in my array that I can add to my current number of 2 to get the target sum of 5. The amount needed to add to the current number to get to the target can be obtained by doing target-currentNumber. In this case, we are currently on 2, so we need to add 3 to get to our target sum of 5. Using the hashmap/object, we can check if we have already seen the number 3. To do this, we can try and access the object 3 key by doing obj[target-currentNumber]. Currently, our object only has the key of '1', so when we try and access the 3 key you'll get undefined. This means we haven't seen the number 3 yet, so, as of now, there isn't anything we can add to 2 to get our target sum.
So now our object/hashmap looks like {'1': 0, '2': 1}, as we have seen the number 1 which was at index 0, and we have seen the number 2 which was at index 1.
Finally, we reach the last number in your array which is at index 2. Index 2 of the array holds the number 3. Now again, we ask ourselves the question: Is there a number we have already seen which we can add to 3 (our current number) to get the target sum?. The number we need to add to 3 to get our target number of 5 is 2 (obtained by doing target-currentNumber). We can now check our object to see if we have already seen a number 2 in the array. To do so we can use obj[target-currentNumber] to get the value stored at the key 2, which stores the index of 1. This means that the number 2 does exist in the array, and so we can add it to 3 to reach our target. Since the value was in the object, we can now return our findings. That being the index of where the seen number occurred, and the index of the current number.
In general, the object is used to keep track of all the previously seen numbers in your array and keep a value of the index at which the number was seen at.
Here is an example of running your code. It returns [1, 2], as the numbers at indexes 1 and 2 can be added together to give the target sum of 5:
const twoSum = function(nums, target) {
const hash = {}; // Stores seen numbers: {seenNumber: indexItOccurred}
for (let i = 0; i < nums.length; i++) { // loop through all numbers
const n = nums[i]; // grab the current number `n`.
if (hash[target - n] !== undefined) { // check if the number we need to add to `n` to reach our target has been seen:
return [hash[target - n], i]; // grab the index of the seen number, and the index of the current number
}
hash[n] = i; // update our hash to include the. number we just saw along with its index.
}
return []; // If no numbers add up to equal the `target`, we can return an empty array
}
console.log(twoSum([1, 2, 3], 5)); // [1, 2]
A solution like this might seem over-engineered. You might be wondering why you can't just look at one number in the array, and then look at all the other numbers and see if you come across a number that adds up to equal the target. A solution like that would work perfectly fine, however, it's not very efficient. If you had N numbers in your array, in the worst case (where no two numbers add up to equal your target) you would need to loop through all of these N numbers - that means you would do N iterations. However, for each iteration where you look at a singular number, you would then need to look at each other number using a inner loop. This would mean that for each iteration of your outer loop you would do N iterations of your inner loop. This would result in you doing N*N or N2 work (O(N2) work). Unlike this approach, the solution described in the first half of this answer only needs to do N iterations over the entire array. Using the object, we can find whether or not a number is in the object in constant (O(1)) time, which means that the total work for the above algorithm is only O(N).
For further information about how objects work, you can read about bracket notation and other property accessor methods here.
You may want to check out this method, it worked so well for me and I have written a lot of comments on it to help even a beginner understand better.
let nums = [2, 7, 11, 15];
let target = 9;
function twoSums(arr, t){
let num1;
//create the variable for the first number
let num2;
//create the variable for the second number
let index1;
//create the variable for the index of the first number
let index2;
//create the variable for the index of the second number
for(let i = 0; i < arr.length; i++){
//make a for loop to loop through the array elements
num1 = arr[i];
//assign the array iteration, i, value to the num1 variable
//eg: num1 = arr[0] which is 2
num2 = t - num1;
//get the difference between the target and the number in num1.
//eg: t(9) - num1(2) = 7;
if(arr.includes(num2)){
//check to see if the num2 number, 7, is contained in the array;
index1 = arr.indexOf(num2);
//if yes get the index of the num2 value, 7, from the array,
// eg: the index of 7 in the array is 1;
index2 = arr.indexOf(num1)
//get the index of the num1 value, which is 2, theindex of 2 in the array is 0;
}
}
return(`[${index1}, ${index2}]`);
//return the indexes in block parenthesis. You may choose to create an array and push the values into it, but consider space complexities.
}
console.log(twoSums(nums, target));
//call the function. Remeber we already declared the values at the top already.
//In my opinion, this method is best, it considers both time complexity and space complexityat its lowest value.
//Time complexity: 0(n)
function twoSum(numbers, target) {
for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] === target) {
return [numbers.indexOf(numbers[i]), numbers.lastIndexOf(numbers[j])];
}
}
}
}

how to check if an element is present in an array or not in javascript without using for loop or any method of array

How to check if an element is present in an array or not in JavaScript without using for loop or any method of array like map, reduce?
let numList= [1,2,3,6,9,2,-8,20]; I want to check for 9 and 30, if it exists or not without using for loop or any array method
I suppose one option would be to convert the array to a Set and check Set.has:
let numList= [1,2,3,6,9,2,-8,20];
const set = new Set(numList);
console.log(set.has(9));
console.log(set.has(30));
Checking whether a Set has an element has complexity of O(1), which is lower complexity than any of the array methods or for loops, which are O(N) or O(log N), so when you have a very large array and you want to check whether it has certain elements, converting it to a Set first can be a good idea.
You can convert the array to string using JSON.stringify and use String includes to check if the string contains the specific searched value
let numList = [1, 2, 3, 6, 9, 2, -8, 20];
let m = JSON.stringify(numList)
console.log(m.includes(-8))
If you can use a while loop you may be able to implement a binary search. Since it was for an interview question I wouldn't be surprised if this is along the lines of what they were looking for.
Here is the traditional algorithm in psuedocode, borrowed from Rosetta Code
BinarySearch(A[0..N-1], value) {
low = 0
high = N - 1
while (low <= high) {
// invariants: value > A[i] for all i < low
value < A[i] for all i > high
mid = (low + high) / 2
if (A[mid] > value)
high = mid - 1
else if (A[mid] < value)
low = mid + 1
else
return mid
}
return not_found // value would be inserted at index "low"
}

Find all pairs that sum to a target value

I've been looking through this example which is a supposedly faster way of matching than using multiple loops. I've seen an explanation here but it makes absolutely no sense to me.
Can someone please break this down for me and what target - arr[i] is been used for?
const arr = [7, 0, -4, 5, 2, 3];
const twoSum = (arr, target) => {
let map = {}
let results = [];
for (let i=0; i<arr.length; i++) {
if (map[arr[i]] !== undefined) {
results.push([map[arr[i]], arr[i]])
} else {
map[target - arr[i]] = arr[i];
}
}
return results;
}
console.log('twoSum = ', twoSum(arr, 5));
Suppose target is t. Given a value x in the array, you want to know if there exists a value t - x in the array, in which case the sum is t - x + x = t.
So you go through the array, to mark the fact you see x in the array you mark the entry t - x in a map. Later when you encounter t - x in the array you check entry t - x in the map, and if it is populated then you know you previously saw x, which means you have the pair x and t - x. The way I just described it sounds like two loops through the array, but you can do these two things in just one loop and it works the same.
If a map entry is populated then you previously saw its pair value, if not populated you mark the map to see if you encounter that pair value later.
You could even make it more faster, without storing of the actual value, because you are looking for a two values and one is known, you know the other as well.
const
arr = [7, 0, -4, 5, 2, 3],
twoSum = (arr, target) => {
let map = {},
results = [];
for (let i = 0; i < arr.length; i++) {
if (map[arr[i]]) { // straight check
results.push([target - arr[i], arr[i]]); // take delta
continue;
}
map[target - arr[i]] = true;
}
return results;
};
console.log('twoSum = ', twoSum(arr, 5));
There seems to be a mistake in the explanation you linked to: where it says, "Our new key/value pair is 5: 5. Our hash map now contains two entries: {7: -2, 5: 5}." The new key/value (and this is achieved correctly in the code) is 5: 0.
To understand how it works suppose our array is [2, 6, 3] and the target is 5. Once we see 2, we'd like to know if the array has its partner that together sums to 5.
x + 2 = 5
x = 5 - 2
x = 3
So we're looking for 3. Now, the JavaScript map object allows us to efficiently retrieve a value if we know its key. So we set our key to 3 - this way if we see a 3 later, we can quickly respond. Remember that we haven't seen 3 yet. We're just setting the key to quickly alert us if we see it that we've seen its partner, 2, already.
Now we continue along the array. We pass by 6 but there's no key 6 in the map so we add it to the map and continue. When we get to 3, we say, "Aha!", the map having 3 is alerting us that we've seen its partner that together sums to 5. We push the result, 3 (the current arr[i]) and the value stored in the map under the 3 key (map[arr[i]]), which was the 2 we saw earlier.
The algorithm is creating pairs by examining the currently processed item with previously seen items.
So, it requires a memory for the previously seen items, and that's why map factors into the solution.
Let's analyse the loop in the solution:
for (let i=0; i<arr.length; i++) {
if (map[arr[i]] !== undefined) {
results.push([map[arr[i]], arr[i]])
} else {
map[target - arr[i]] = arr[i];
}
}
It's equivalent to the following:
for ( let i = 0; i < arr.length; i++ ) {
// Any item in the array that's not in the memory
// 1. should primarily be stored
// 2. such that it's potential pair is stored as a key that's mapped to a value which is the item
if ( map[ arr[ i ] ] === undefined ) {
map[ target - arr[ i ] ] = arr[ i ];
// Examine pairs only in iterations with odd numbered indices.
// Why? - For the first iteration, the memory is empty...
continue;
}
// this item’s pair is known, so store the pair in the result list
results.push( [ map[ arr[ i ] ], arr[ i ] ] );
}

Javascript Arrays - Find Duplicates [duplicate]

This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 8 years ago.
Here is my question...
Given an array populated with numbers as a function parameter, produce a resulting array which contains any duplicates number from the array.
For example, given the array [ 1, 2, 4, 4, 3, 3, 1, 5, 3 ] it should return [1, 4, 3]. For extra bonus points return a sorted array.
I am starting out with Javascript - I know the language however, using it in the correct way ( as one should ) I'm still getting to grips with.
My pseudo code for this would be to:
Create an array with the numbers above var numbers = [1, 2, 4, 4, 3, 3, 1, 5, 3];
Then create an empty array named "result" var result = [];
Create a for loop that goes through the var numbers to check for duplicates which will then populate the empty array "result" with the duplicates
for (var i = 0;i < numbers.length; i++) {
//This is where I'm stuck...
}
I'm not sure what to do within the for loop to populate the var result and to throw in to the mix... The given array has to be a function parameter which makes sense so you can change the numbers in one place.
Any feedback on my thought process on this so far is greatly appreciated but ultimately I am wanting to learn how to achieve this.
Here is a JSFiddle of my progress so far... http://jsfiddle.net/fbauW/
One way of doing this (and it's not the only way) is by checking for existing elements in the array. Take a look at JavaScript's lastIndexOf function:
http://www.w3schools.com/jsref/jsref_lastindexof_array.asp
It will return -1 if the object does not exist in your array, and if it exists, will return an index of a later position than you are in. So you can use an if statement in your loop that checks whether or not there is another index containing your number, and add it in to your results array IF AND ONLY IF the index you get back != the index you are currently on (if they equal, this means that there is only one of that element in the list).
If you need more help, comment here and I can type some code in!
Good luck!
Array.prototype.contains = function(k) {
for ( var p in this)
if (this[p] === k)
return true;
return false;
};
//this prototype function checks if an element is already in the array or not
//go through all the array and push the element to result if it is not
//this way we can eliminate duplicates
//result will contain the resultant array
function findDuplicates(Numbers) {
var arrayLength = Numbers.length, i, j, result = [];
for (i = 0; i < arrayLength; i++) {
for (j = 0; j < arrayLength; j++) {
if (a[i] == a[j] && i != j && !result.contains(a[i])) {
result.push(a[i]);
}
}
}
return result;
}

Find a pair of elements from an given array whose sum equals a specific target number in JavaScript

In Javascript is any other efficient way to achieve this task?
I tried as:
const a1 = [1,3,4,2,5,7,8,6];
var newArray =[];
function fun(a,n){
for(let i = 0; i<a.length; i++){
for(let j=i+1; j<a.length; j++){
if((a[i]+a[j])==n){
newArray.push([a[i],a[j]]);
}
}
}
}
fun(a1, 10)
console.log(newArray);
Here output:
[(3,7),(4,6),(2,8)]
The question is tagged javascript, but this answer is basically language agnostic.
If the array is sorted (or you can sort it), you can iterate over the array and for each element x in it binary search for (target-x) in the array. This gives you O(nlogn) run time.
If you can use extra memory, you can populate a dictionary with the elements of the array, and then for each element x in the array, look up the dictionary for (target-x). If your dictionary is implemented on a hashtable, this gives you O(n) run time.
I think your method makes sense from a brute force perspective.
In terms of optimization, a few things come to mind.
Do duplicates count? If not, you can remove all duplicates from
the starting lists.
You can sort the lists in ascending order,
and skip the remainder of the inner loop when the sum exceeds the
target value.
This is a general programming problem commonly referred to as the "two sum problem", which itself is a subset of the subset sum problem.
But nevertheless can be solved efficiently and I used this article as inspiration.
const a1 = [1, 3, 4, 2, 5, 7, 8, 6];
// our two sum function which will return
// all pairs in the array that sum up to S
function twoSum(arr, S) {
const sums = [];
const hashMap = new Map();
// check each element in array
for (let i = 0; i < arr.length; i++) {
// calculate S - current element
let sumMinusElement = S - arr[i];
// check if this number exists in hash map
// if so then we found a pair of numbers that sum to S
if (hashMap.has(sumMinusElement.toString())) {
sums.push([arr[i], sumMinusElement]);
}
// add the current number to the hash map
hashMap.set(arr[i].toString(), arr[i])
}
// return all pairs of integers that sum to S
return sums;
}
console.log(twoSum(a1, 10))
Here I use the Map object since I think it's faster when checking if number already exists, but I might be wrong and you can use just a plain object, as in the article, if you want.

Categories

Resources