I want a help in optimising a solution of a problem, I already sort out the problem, but my code is not good enough for handling large array -
codeWars : Sum of Pairs - problem
Here is my code -
var sum_pairs=function(e, sum){
var result=null;
var arrLen=e.length;
for(let i=0;i<arrLen-1;i++){
let nextIndex=e.slice(i+1,arrLen).indexOf(sum-e[i]);
if(nextIndex>=0){
result=[e[i],e[nextIndex+1+i]];
arrLen=nextIndex+1+i;
}
}
return result;
}
Well, I know this is not a good solution. Anyway, this passes all the test cases but failed when it encounter large array -
Result On codewars
I want to know how to optimise this code, and also Learn any technique to writing a good code.
One solution is to use Set data structure to memorize the numbers all ready iterated over. Then we can check for each element if there has been a number which sums to s. The set has an average constant time complexity for insert and search making the algorithm linear in time (and space).
var sum_pairs=function(ints, s){
if (ints.length < 2) return undefined; //not enough numbers for pair.
let intSet = new Set()
intSet.add(ints[0]);
for (let i=1; i < ints.length; ++i){
let needed = s-ints[i];
if (intSet.has(needed)){//check if we have already seen the number needed to complete the pair.
return [needed,ints[i]];
}
intSet.add(ints[i]);//if not insert the number in set and continue.
}
return undefined;//No answer found
}
function sumPairs (ints, s) {
if (ints.length<2) return undefined
let inSet = new Set()
for (let i= 0;i<ints.length;i++){
let need = s-ints[i]
if( inSet.has(need)){
return [need,ints[i]]
}
inSet.add(ints[i])
}
return undefined
}
Related
I want to partition an array (eg [1,2,3,4,5,6,7,8]), first partition should keep even values, second odd values (example result: [2,4,6,8,1,3,5,7]).
I managed to resolve this problem twice with built-in Array.prototype methods. First solution uses map and sort, second only sort.
I would like to make a third solution which uses a sorting algorithm, but I don't know what algorithms are used to partition lists. I'm thinking about bubble sort, but I think it is used in my second solution (array.sort((el1, el2)=>(el1 % 2 - el2 % 2)))... I looked at quicksort, but I don't know where to apply a check if an integer is even or odd...
What is the best (linear scaling with array grow) algorithm to perform such task in-place with keeping order of elements?
You can do this in-place in O(n) time pretty easily. Start the even index at the front, and the odd index at the back. Then, go through the array, skipping over the first block of even numbers.
When you hit an odd number, move backwards from the end to find the first even number. Then swap the even and odd numbers.
The code looks something like this:
var i;
var odd = n-1;
for(i = 0; i < odd; i++)
{
if(arr[i] % 2 == 1)
{
// move the odd index backwards until you find the first even number.
while (odd > i && arr[odd] % 2 == 1)
{
odd--;
}
if (odd > i)
{
var temp = arr[i];
arr[i] = arr[odd];
arr[odd] = temp;
}
}
}
Pardon any syntax errors. Javascript isn't my strong suit.
Note that this won't keep the same relative order. That is, if you gave it the array [1,2,7,3,6,8], then the result would be [8,2,6,3,7,1]. The array is partitioned, but the odd numbers aren't in the same relative order as in the original array.
If you are insisting on an in-place approach instead of the trivial standard return [arr.filter(predicate), arr.filter(notPredicate)] approach, that can be easily and efficiently achieved using two indices, running from both sides of the array and swapping where necessary:
function partitionInplace(arr, predicate) {
var i=0, j=arr.length;
while (i<j) {
while (predicate(arr[i]) && ++i<j);
if (i==j) break;
while (i<--j && !predicate(arr[j]));
if (i==j) break;
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
return i; // the index of the first element not to fulfil the predicate
}
let evens = arr.filter(i=> i%2==0);
let odds = arr.filter(i=> i%2==1);
let result = evens.concat(odds);
I believe that's O(n). Have fun.
EDIT:
Or if you really care about efficiency:
let evens, odds = []
arr.forEach(i=> {
if(i%2==0) evens.push(i); else odds.push(i);
});
let result = evens.concat(odds);
Array.prototype.getEvenOdd= function (arr) {
var result = {even:[],odd:[]};
if(arr.length){
for(var i = 0; i < arr.length; i++){
if(arr[i] % 2 = 0)
result.odd.push(arr[i]);
else
result.even.push(arr[i]);
}
}
return result ;
};
I have searched high and low, not only on StackOverflow, but many other places elsewhere on the web. I've tried what seems like everything, but something is fundamentally flawed with my logic. I apologize for introducing another "Duplicates in Array" question, but I am stuck and nothing seems to be working as expected.
Anyway, I have a multi-dimensional JavaScript array, only 2 levels deep.
var array = [[Part #, Description, Qty:],
[Part #, Description, Qty:],
[Part #, Description, Qty:]]; //etc
What I need to do is create a function that searches array and returns any duplicate "Part #" lines. When they are returned, I would like to have the entire inner array returned, complete with description and qty.
The trick with this is that the Part #'s that would qualify as 'duplicate' would end differently (specifically the last 4 characters), so using String.prototype.substr makes sense (to me).
I know there are duplicates in the array in the way that I am looking for, so I know that if I had the solution, it would return those Part #'s.
Here is what I have tried so far that gets me the closest to a solution:
function findDuplicateResults(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i][0].substr(0,5) === arr[++i][0].substr(0,5)) {
result.push(arr[i]);
}
}
return console.log(result);
}
My thinking is that if the element in the array(with substr(0,5) is equal to the next one in line, push that to the result array. I would need the other duplicate in there too. The point of the code is to show only dupes with substr(0,5).
I have tried using Higher Order Functions such as map, forEach, reduce, and filter (filter being the one that boggles my mind as to why it doesn't do what I want), but I have only been able to return [] or the entire array that way. The logic that I use for said Higher Order Functions remains the same (which is probably the problem here).
I am expecting that my if condition is where the most of the problem is. Any pointers or solutions are greatly appreciated.
There is a mistake in your code. When you use ++i, you are changing the value of i, so it is going to skip one item in the next iteration.
Regarding the logic, you are only comparing one item to the next item, when you should really be comparing each item to all items:
function findDuplicateResults(arr) {
var result = [];
for (var i = 0; i <= arr.length - 1; i++) {
for (var k = 0; k <= arr.length - 1; k++) {
if (i !== k && arr[i][0].substr(0,5) === arr[k][0].substr(0,5)) {
result.push(arr[i]);
}
}
}
return result;
}
Although, the 'substr' could be dropped, and 'for' loop could be replaced by a higher order function:
function findDuplicateResults(arr) {
return arr.filter(function(item1){
return arr.filter(function(item2){
return item1[0] === item2[0];
}).length > 1;
});
}
I'm studying for an interview and have been working through some practice questions. The question is:
Find the most repeated integer in an array.
Here is the function I created and the one they created. They are appropriately named.
var arr = [3, 6, 6, 1, 5, 8, 9, 6, 6]
function mine(arr) {
arr.sort()
var count = 0;
var integer = 0;
var tempCount = 1;
var tempInteger = 0;
var prevInt = null
for (var i = 0; i < arr.length; i++) {
tempInteger = arr[i]
if (i > 0) {
prevInt = arr[i - 1]
}
if (prevInt == arr[i]) {
tempCount += 1
if (tempCount > count) {
count = tempCount
integer = tempInteger
}
} else {
tempCount = 1
}
}
console.log("most repeated is: " + integer)
}
function theirs(a) {
var count = 1,
tempCount;
var popular = a[0];
var temp = 0;
for (var i = 0; i < (a.length - 1); i++) {
temp = a[i];
tempCount = 0;
for (var j = 1; j < a.length; j++) {
if (temp == a[j])
tempCount++;
}
if (tempCount > count) {
popular = temp;
count = tempCount;
}
}
console.log("most repeated is: " + popular)
}
console.time("mine")
mine(arr)
console.timeEnd("mine")
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
These are the results:
most repeated is: 6
mine: 16.929ms
most repeated is: 6
theirs: 0.760ms
What makes my function slower than their?
My test results
I get the following results when I test (JSFiddle) it for a random array with 50 000 elements:
mine: 28.18 ms
theirs: 5374.69 ms
In other words, your algorithm seems to be much faster. That is expected.
Why is your algorithm faster?
You sort the array first, and then loop through it once. Firefox uses merge sort and Chrome uses a variant of quick sort (according to this question). Both take O(n*log(n)) time on average. Then you loop through the array, taking O(n) time. In total you get O(n*log(n)) + O(n), that can be simplified to just O(n*log(n)).
Their solution, on the other hand, have a nested loop where both the outer and inner loops itterate over all the elements. That should take O(n^2). In other words, it is slower.
Why does your test results differ?
So why does your test results differ from mine? I see a number of possibilities:
You used a to small sample. If you just used the nine numbers in your code, that is definately the case. When you use short arrays in the test, overheads (like running the console.log as suggested by Gundy in comments) dominate the time it takes. This can make the result appear completely random.
neuronaut suggests that it is related to the fact that their code operates on the array that is already sorted by your code. While that is a bad way of testing, I fail to see how it would affect the result.
Browser differences of some kind.
A note on .sort()
A further note: You should not use .sort() for sorting numbers, since it sorts things alphabetically. Instead, use .sort(function(a, b){return a-b}). Read more here.
A further note on the further note: In this particular case, just using .sort() might actually be smarter. Since you do not care about the sorting, only the grouping, it doesnt matter that it sort the numbers wrong. It will still group elements with the same value together. If it is faster without the comparison function (i suspect it is), then it makes sense to sort without one.
An even faster algorithm
You solved the problem in O(n*log(n)), but you can do it in just O(n). The algorithm to do that is quite intuitive. Loop through the array, and keep track of how many times each number appears. Then pick the number that appears the most times.
Lets say there are m different numbers in the array. Looping through the array takes O(n) and finding the max takes O(m). That gives you O(n) + O(m) that simplifies to O(n) since m < n.
This is the code:
function anders(arr) {
//Instead of an array we use an object and properties.
//It works like a dictionary in other languages.
var counts = new Object();
//Count how many of each number there is.
for(var i=0; i<arr.length; i++) {
//Make sure the property is defined.
if(typeof counts[arr[i]] === 'undefined')
counts[arr[i]] = 0;
//Increase the counter.
counts[arr[i]]++;
}
var max; //The number with the largest count.
var max_count = -1; //The largest count.
//Iterate through all of the properties of the counts object
//to find the number with the largerst count.
for (var num in counts) {
if (counts.hasOwnProperty(num)) {
if(counts[num] > max_count) {
max_count = counts[num];
max = num;
}
}
}
//Return the result.
return max;
}
Running this on a random array with 50 000 elements between 0 and 49 takes just 3.99 ms on my computer. In other words, it is the fastest. The backside is that you need O(m) memory to store how many time each number appears.
It looks like this isn't a fair test. When you run your function first, it sorts the array. This means their function ends up using already sorted data but doesn't suffer the time cost of performing the sort. I tried swapping the order in which the tests were run and got nearly identical timings:
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
console.time("mine")
mine(arr)
console.timeEnd("mine")
most repeated is: 6
theirs: 0.307ms
most repeated is: 6
mine: 0.366ms
Also, if you use two separate arrays you'll see that your function and theirs run in the same amount of time, approximately.
Lastly, see Anders' answer -- it demonstrates that larger data sets reveal your function's O(n*log(n)) + O(n) performance vs their function's O(n^2) performance.
Other answers here already do a great job of explaining why theirs is faster - and also how to optimize yours. Yours is actually better with large datasets (#Anders). I managed to optimize the theirs solution; maybe there's something useful here.
I can get consistently faster results by employing some basic JS micro-optimizations. These optimizations can also be applied to your original function, but I applied them to theirs.
Preincrementing is slightly faster than postincrementing, because the value does not need to be read into memory first
Reverse-while loops are massively faster (on my machine) than anything else I've tried, because JS is translated into opcodes, and guaranteeing >= 0 is very fast. For this test, my computer scored 514,271,438 ops/sec, while the next-fastest scored 198,959,074.
Cache the result of length - for larger arrays, this would make better more noticeably faster than theirs
Code:
function better(a) {
var top = a[0],
count = 0,
i = len = a.length - 1;
while (i--) {
var j = len,
temp = 0;
while (j--) {
if (a[j] == a[i]) ++temp;
}
if (temp > count) {
count = temp;
top = a[i];
}
}
console.log("most repeated is " + top);
}
[fiddle]
It's very similar, if not the same, to theirs, but with the above micro-optimizations.
Here are the results for running each function 500 times. The array is pre-sorted before any function is run, and the sort is removed from mine().
mine: 44.076ms
theirs: 35.473ms
better: 32.016ms
I understand how to go about tasks using loops, recursion is kind of a mystery to me, but from what I understand in certain cases it can save a ton of time if looping through a lot of data.
I created the following function to loop through a large(ish) data set.
var quotes = require('./quotes.js');
//Pulls in the exported function from quotes.js
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
//Create an empty key value object, we use these to coerce unique values to an array
var uniqs = {};
//I create this object to hold all the authors and their quotes
var fullQuote = {};
//Create an object with only unique authors
for(var i = 0; i < allAuthors.length ; i++){
fullQuote[allAuthors[i]] = null;
}
//Coerce unique authors from javascript object into an array
var uniqAuthors = Object.keys(uniqs);
var quoteCount = exportedQuotes.author.length;
var iterativeSolution = function(){
for(var i = 0; i < Object.keys(fullQuote).length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(Object.keys(fullQuote)[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
}
I don't currently have the skills to do analyze this, but, I'm wondering if there is a case for recursion to save the time it takes to get through all the loops. And if there is a case for recursion what does it look like for nested loops in javascript, specifically when creating key value objects recursively?
There may be a slight misunderstanding about what recursion is: recursion does not save time. It's just a different way of doing the same traversal. It generally a little easier to read, and depending on the problem, will map to certain algorithms better. However, one of the first things we do when we need to start optimizing code for speed is to remove recursion, turning them back into loops, and then even "unrolling" loops, making code much uglier, but fast, in the process. Recursion vs plain loops is almost always a matter of taste. One looks nicer, but that's hardly the only quality we should judge code on.
And also: just because it sounds like I'm advocating against using it, doesn't mean you shouldn't just try it: take that code, put it in a new file, rewrite that file so that it uses recursion. Doing so lets you compare your code. Which one is faster? Which is easier to read? Now you know something about how (your) code behaves, and you'll have learned something valuable.
Also don't sell yourself short: if you wrote this code, you know how it works, so you know how to analyze it enough to rewrite it.
Algorithms makes code fast or slow, not recursion. Some quite fast algorithms can use recursion, but that's a whole different story. Many algorithms can be written as both with recursion, and without recursion.
However, your code has a big problem. Notice how many times you call this code?
Object.keys(fullQuote)
You are re-computing the value of that many many times in your code. Don't do that. Just call it once and store in a variable, like the following:
var uniqAuthors = Object.keys(uniqs);
var uniqFullQuote = Object.keys(fullQuote);
var quoteCount = exportedQuotes.author.length;
//Loop through all quotes and assign all quotes to a unique author::Each author has many quotes
for(var i = 0; i < uniqFullQuote.length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(uniqFullQuote[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
You don't have to iterate Object.keys(fullQuote).
var quotes = require('./quotes.js'),
exportedQuotes = quotes.allQuotes(),
allAuthors = exportedQuotes.author,
fullQuote = Object.create(null);
for(var i=0; i < allAuthors.length; ++i)
(fullQuote[allAuthors[i]] = fullQuote[allAuthors[i]] || [])
.push(exportedQuotes.quote[i])
I don't recommend recursion. It won't improve the asymptotic cost, and in JS calling functions is a bit expensive.
I got really curious and created a recursive solution just to see how it works. Then timed it, my iterative solution took 53 seconds to run, while my recursive solution took 1 millisecond to run. The iterative approach can obviously be tweaked based on the answers provided below, to run faster, but a recursive approach forced me to think in a "leaner" manner when creating my function.
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
var n = allAuthors.length
var fullQuote = {};
var recursiveSolution = function(arrayLength) {
//base case
if(arrayLength <= 1){
if(fullQuote[exportedQuotes.author[0]] == null){
fullQuote[exportedQuotes.author[0]] = [exportedQuotes.quote[0]];
}else{
fullQuote[exportedQuotes.author[0]].push(exportedQuotes.quote[0])
}
return;
};
//recursive step
if(fullQuote[exportedQuotes.author[arrayLength]] == null){
fullQuote[exportedQuotes.author[arrayLength]] = [exportedQuotes.quote[arrayLength]];
}else{
fullQuote[exportedQuotes.author[arrayLength]].push(exportedQuotes.quote[arrayLength])
}
newLength = arrayLength - 1;
return recursiveSolution(newLength);
}
////////Timing functions
var timeIteration = function(){
console.time(iterativeSolution);
iterativeSolution(); // run whatever needs to be timed in between the statements
return console.timeEnd(iterativeSolution);
}
var timeRecursive = function(){
console.time(recursiveSolution(n));
recursiveSolution(n); // run whatever needs to be timed in between the statements
return console.timeEnd(recursiveSolution(n));
}
Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/