Can someone explain why this does not work? - javascript

I'm going through the interview cake algorithms. This is basically self-assigned homework. I've given it a few goes, and my solution is fairly ugly, but I don't understand why it's not giving me the correct output. Only one loop executes in the algorithm, and I've tried switching the order, as well as other versions along this line.
I'm a little stuck here, and have two questions relating to this algorithm, which I'm trying to complete in straight-up Javascript.
Here's the problem:
You have an array of integers, and for each index you want to find the product of every integer except the integer at that index. Write a function get_products_of_all_ints_except_at_index() that takes an array of integers and returns an array of the products. Do not use division.
The array [1, 7, 3, 4]
Would return:
[84, 12, 28, 21]
By calculating:
[7*3*4, 1*3*4, 1*7*4, 1*7*3]
My overwrought attempt is below:
var l = [84, 12, 28, 21],
products_before_curr_index = 1,
products_after_curr_index = 1,
backwards_index=1,
forwards_index,
product_array = [];
for(var factor=0; factor<l.length; factor++){
forwards_index=factor+1;
while(forwards_index<l.length){
products_after_curr_index*=l[forwards_index];
forwards_index+=1;
}
if(factor>0){
products_before_curr_index *= l[factor-backwards_index];
backwards_index+=1;
}
product_array.push(products_after_curr_index*products_before_curr_index);
backwards_index=1;
products_after_curr_index = 1;
products_before_curr_index=1;
}
This returns [84, 12, 28, 3]
My questions:
Why doesn't this work? Where am I going wrong?
There must be a more elegant way to do this using slice or map. I'm stumped. Can someone give me a hint here?

This one uses map and reduce methods of Array object.
function get_products_of_all_ints_except_at_index(inList) {
var product = function (x, y) { return x * y; };
var lists = inList.map(function (v, i, a) {
return a.slice(0, i).concat(a.slice(i + 1, a.length));
});
return lists.map(function (v, i, a) { return v.reduce(product); });
}
// test case
console.log(get_products_of_all_ints_except_at_index([1, 7, 3, 4]));

Yeah it looks like you have overcomplicated it a bit. The problem description gives you a bit of a hint on how to solve the problem. For each integer in the array, you want to find the product of all the other integers. In other words, you can loop over each item in the array, then loop over each item in the array again (aka, a nested loop) and add up all the products except for the current index of the first array.
For example:
var a = [1, 7, 3, 4];
var b = [];
a.forEach(function( value_1 ) {
var product = 1;
a.forEach(function( value_2 ) {
if ( value_1 != value_2 )
product *= value_2;
});
b.push( product );
});
console.log( b ); // [84, 12, 28, 21]
Your original solution doesn't work because you only ever go back once. You use a while loop to go forward through the entire array to calculate the product of all the elements after, but you only use one single calculation to go backwards:
products_before_curr_index *= l[factor-backwards_index];
So you're only ever getting the product of the value immediately before the current index. You never go back further. If you're wondering, you only happen to get the third value (28) right because the first number is 1 (aka, multiplying by 1 wouldn't have done anything anyway). Try changing that first number to anything that isn't 1 and you'll see that the third calculation also fails.

I didn't particularly liked the solution given by Interview Cake, I felt it was complicated. I may not have the most optimized solution, and I used Numpy which is not clear if we can (but it doesn't say we can't, so...)
It worked for all test cases and I feel it's way more simple/easy to grasp:
` import numpy as np
def get_products_of_all_ints_except_at_index(int_list):
if len(int_list) < 2:
raise IndexError("you need two numbers at least")
products = []
for i in range(len(int_list)):
temp_list = int_list.copy()
del temp_list[i]
prod = np.prod(temp_list)
products.append(prod)
return products

Related

Using a loop to increment each slot of an array by 1. The size of the array is the argument passed into a function

i am trying to solve a code challenge that asks me to fill an array with the value passed as an argument in a function.
For example = fizzBuzz(10)
should return an Array with 10 slots and for each slot increment 1
[0, 1, 2, 3 ,4, 5, 6, 7, 8, 9, 10]
i have tried with a loop and with the fill method but i am having difficulties on this.
This is the first step of the algorithm. Can someone help me ?
Here is my last attempt:
function fizzbuzz(n) {
// Write your code here
const array = new Array(n)
for(let i = 0; i < n; i++) {
array.fill(n, 0))
}
return array
}
This wont work cause the fill method is only called once for every slot i guess. Can someone help me ?
I have tried with the forEach method, fill method and with a loop, but i am unable to solve the first step of this algorithm.
I need nelp solving the first step of this algorithm.
So: you are learning to use arrays.
Let's start with the basics.
An array is a kind of variable that points to a list of things.
Javascript simulates traditional arrays by numbering each element:
const array = new Array(10);
But javascript is not vary rigid about how many elements are in an array, so you could also say:
const array = [];
There's a difference between these two, even though absolutely nothing is in it yet: the first form has 10 undefined values. The second form as no values and a length of 0. But they both work just fine.
How do you access elements in this list? By number!
console.log(array[0]);
In both of the above cases, you are going to get "undefined" as a result.
Now to understand your homework, all you need to know is how to assign values to the array. The array object method "fill" is no good, because what your initial loop does is assign ALL elements to 0, and then to 1, and then to 2, etc. That's no good. Who knows why we even have a "fill" method. I've never used it in my life.
But what happens ALL THE TIME is:
const array = [];
for (let i=0; i < n; i++) {
array[i] = i;
}
What is happening here?
The element in the list at position 0 is set to 0.
The element in the list at position 1 is set to 1.
And so on.
Because it is javascript, there are actually MANY ways to do this. This is just one. Using "fill" is not one. But this is the most fundamental pattern common across most languages, so it is a good one to master before moving on to fancier approaches.
If you are trying to created an Array of size N with values from 0...N then the size of the array should be N + 1.
For initializing the array
You can use ES6 using Array
keys() and spread operator.
const N = 10;
const array = [...Array(N+1).keys()]
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
If you want to use for loop, then you can populate the array like this:
const N = 10;
const array = new Array(N+1);
for(let i=0; i<array.length; i++) {
array[i] = i;
}
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
How to create an array containing 1...N

How can I move elements from one array to another pseudo-randomly using a hash?

Picture two different arrays:
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let newArr = [];
What I'm trying to achieve is moving elements from arr to newArr via a pseudo-random loop, that attempts to mimic the functionality of Math.random(), except that the only randomness should go against a fixed, hash number.
I was trying something like this, but no luck so far:
const madeUpHash = "827354819373"
const floatIndex = "0."
const f = parseFloat(floatIndex + madeUpHash);
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let newArr = [];
for (let i = 0; i < arr.length; i++) {
let ranNum = (f/i); // this points towards 0, meaning the next Math.Floor will eventually always retrieve index 0
let rand = Math.floor(ranNum * arr.length);
newArr.push(...arr.splice(rand, 1))
}
Not only does this not work, it's also pretty horrible.
Is there any reliable way to achieve this?
My other idea would be to loop through the individual digits of the hash (after transforming them into an array) and create a random number such as ranNum = i/madeUpHash[i] but that would break as soon as i >= madeUpHash[i] since it would return a positive integer bigger than one.
Any ideas? Thank you.
I'm not sure about how people are implementing Math.random in javascript, but I know it in C.
Basically, the idea is, you got a hash function hash, and a seed s, you apply hash(s) to get your X_0, and for X_(n+1), you do X_(n+1) = hash(X_n).
As long as you choose a good hash function with the certain distribution you want for your randomness, it's very easy and consistent if the seed is the same.
For C, they are doing Linear Congruential Generator
X_(n+1) = (a * X_n + b) % m
So you can use your hash number as a seed and generate a series of pseudo-random numbers. Basically, you need a place to store your seed and replace it when you call the random generator (and don't forget to apply the hash function).
The randomness totally comes from your choice of hash function and it's not easy to prove you get a good hash function to generate random numbers (like we want a uniform distribution hash function in most cases, and the performance matters too). But I believe there is literature to do those proofs for you.
Why do you divide ranNum by i?
let rand = Math.floor(f * arr.length);
Should be ok, because you decreasearr.length in each iteration. Also I'm not sure about incrementing i and removing an element from array, which affects the loop condition at the same time. Maybe just use the while loop.

How to find a number that occurs the most in an array in less than O(n)?

I've been working on some Coding Katas to improve my programming. The question I have is how can I solve this problem with less than O(n). My first thought was to use regex, but I ran into some problems as outlined below.
Problem Definition
Using the JavaScript language, have the function SimpleMode(arr) take
the array of numbers stored in arr and return the number that appears
most frequently (the mode). For example: if arr contains [10, 4, 5, 2,
4] the output should be 4. If there is more than one mode return the
one that appeared in the array first (ie. [5, 10, 10, 6, 5] should
return 5 because it appeared first). If there is no mode return -1.
The array will not be empty.
O(n) Solution
Here is the O(n) solution that I was able to come up with:
function simpleMode(set) {
var counter = {};
var most = set.reduce(function(most, value) {
if (!counter[value]) {
counter[value] = 0;
}
counter[value]++;
if (!counter[most] || counter[value] > counter[most]) {
most = value;
}
return most;
}, -1);
return (counter[most] === 1 ? -1 : most);
};
console.log(simpleMode([5, 2, 5, 2, 3, 1]));
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
Regex Solution so far:
The problem with this solution is that it doesn't meet the requirement of returning the value that first appeared with the greatest occurrence. As well, I feel like using sort is a bit of a cheat, but it should still be less than O(n).
function simpleMode(set) {
var values = set.sort().join('')
var regex = new RegExp(/(\d)\1+/g);
var matches = regex.exec(values);
return (matches ? matches[1] : -1);
}
console.log(simpleMode([5, 2, 5, 2, 3, 1]));
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
Summary
How can this problem be solved with less than O(n)?
How could the regex solution be fixed to work?
Such an algorithm is impossible. Suppose any algorithm looks at fewer than n/2 elements of the array. Then if the elements that haven't been looked at are all the same, they'll be the most-common value.
So any algorithm that finds the most common element must always look at n/2 elements of the array, and so its time complexity must be at least O(n).

Iterate through array backwards from index in JavaScript

I have a bit of Python code that does what I want very nicely, but trying to port that across to JavaScript is proving difficult to do cleanly.
jsonLine = [[0,1],[2,4],[4,8],[9,12],[11,16],[12,13]]
[firstX, firstY] = [9,12]
if [firstX, firstY] in jsonLine:
index = jsonLine.index([firstX, firstY])
lineArray.append(jsonLine[index:])
lineArray.append(jsonLine[index::-1])
jsonLine is an array of coordinates which make up a line, and [firstX, firstY] is the starting point on a line, defined by a user. I'm creating a script that creates two lines, one in either direction from the point the user chooses, which will later be cut shorter based on distance from the user's point.
The desired output in this case will be:
[[[9,12],[11,16],[12,13]],[[9,12],[4,8],[2,4],[0,1]]]
Below is the JavaScript I have, which gets the first of the desired arrays, but using a for loop doesn't feel right, and if I use jsonLine.reverse().slice(featureArray.length-vertex), it seems to duplicate the arrays pushed to lineArray. Is there a cleaner way to slice an array and reverse it?
for (var vertex = 0; vertex < featureArray.length; vertex++){
if (jsonLine[vertex][0] === firstX && jsonLine[vertex][1] === firstY) {
console.log(jsonLine.slice(vertex))
}
You can create a find method to find out the index of the coordinates you're interested in. After that, apply slice and reverse to get the format you're looking for:
var jsonLine = [
[0, 1],
[2, 4],
[4, 8],
[9, 12],
[11, 16],
[12, 13]
],
el = [9, 12],
index, res;
function findElement() {
var i = 0;
for (; i < jsonLine.length; i += 1) {
if (jsonLine[i][0] === el[0] && jsonLine[i][1] === el[1]) {
return i;
}
}
return -1;
}
index = findElement();
res = [
[jsonLine.slice(index)],
[jsonLine.reverse().slice(index - 1)]// -1 because you want to repeat the element.
];
console.log(res);
Fiddle
Note: As #RobG points out in the comments of this answer, if you want to keep the array intact, substitute the second part by jsonLine.slice(0, index + 1).reverse(). Reverse modifies the original array and that might be undesired behaviour (though it wasn't clear in the question whether it was or not).

multiple arrays into one array

I need help with the five.myArraysCombined property.
I need it to equal just 1 array (which it currently does in fiddle) and I need it to NOT add any numbers together. (so each number in the array shouldn't be over 20, just like no number in the other arrays are over 20)
http://jsfiddle.net/Dc6HN/1/
For example, if the five arrays are like this
five.myArray1 = [7,2,9,19,3];
five.myArray2 = [6,18,8,1,7];
five.myArray3 = [7,19,4,8,2];
five.myArray4 = [11,9,1,14,5];
five.myArray5 = [3,18,8,9,2];
then the all those arrays combined should be like this
five.myArraysCombined = [7,2,9,19,3,6,18,8,1,7,7,19,4,8,2,11,9,1,14,5,3,18,8,9,2];
Relevant code :
function theNumberClass() {
this.myArray = [[],[],[],[],[]];
this.myArraysCombined = [];
}
var five = new theNumberClass();
function prePickNumbers(objName, theNum, theSumNum, theMaxNum, theMinNum) {
var zzz = [];
for (var x = 0; x < theNum; x += 1) {
pickNumbers(objName.myArray[x], theNum, theSumNum, theMaxNum, theMinNum);
zzz += objName.myArray[x];
}
objName.myArraysCombined.push(zzz);
}
prePickNumbers(five, 5, 40, 20, 1);
My latest attempt was with var zzz and then pushing it to the property, but when I do that it adds up the numbers in the array at times, which is not what I need.
I've also tried several attempts using the .concat(), but it seems to turn it into a string and sometimes also adds up the numbers.
Suppose you have those arrays :
var a = [1, 2, 3]
var b = [4, 5, 6]
var c = [8]
Then you can get a merge of all those with
var all = [].concat.apply([],[a,b,c])
or with
var all = [a,b,c].reduce(function(merged, arr){ return merged.concat(arr) })
In both cases you get
[1, 2, 3, 4, 5, 6, 8]
The first solution is simpler, the second one is more extensible if you want, for example, to remove duplicate or do any kind of filtering/transformation.
I would guess that the issue is the "+=" operator. This operator is used to sum values, not add new elements to an array. Take the following line of code as an example:
zzz += objName.myArray[x];
What I am guessing is that "myArray[x]" is getting added to the value of zzz instead of getting appended to the end of the array. When adding elements to an array in javascript, push is the best option. A better way to write this line is:
zzz.push(objName.myArray[x]);
The question was a bit confusing so I'm not sure if this is what you are looking for but hopefully it will help anyways.
five.reduce(function(o,n){return o.concat(n)},[])
This will reduce the array to a single value, in this case an array of numbers. You can look up Array.reduce() on MDN for more info.
After many hours trying all suggestions left on this thread and another one, and trying multiple other things. I think I finally found a very simple way to do this. And it's the only way I tried that works 100% like I want.
http://jsfiddle.net/Dc6HN/2/
function prePickNumbers(objName, theNum, theSumNum, theMaxNum, theMinNum) {
for (var x = 0; x < theNum; x += 1) {
pickNumbers(objName.myArray[x], theNum, theSumNum, theMaxNum, theMinNum);
objName.myArraysCombined.push(objName.myArray[x]);
}
objName.myArraysCombined = objName.myArraysCombined.toString();
objName.myArraysCombined = objName.myArraysCombined.split(',');
}

Categories

Resources