Sort array by range of static values - javascript

I have an array, for simplicity, lets say it is this:
var array = [2,55, 22, 6, 7];
and I want to sort it, I would do this:
array.sort(function (a, b) {
return b > a ? -1: 1;
});
or if I want to sort it ascending I would do this:
array.sort(function (a, b) {
return b < a ? -1: 1;
});
Now, let's say I have a value that I want to sort by.
So basically I want to sort my array by values greater than a number.
so if the number was 6, I would want my array to look something like this:
55, 22, 7, 6, 2
but if I want to sort between two numbers, let's say 6 and 23, I would want it to return something like this:
22, 7, 55, 6, 2
the last 3 items can appear in any order to be honest, but the range I have sorted must appear first.
Does anyone know how I can achieve this.
I have tried like this:
// Sort our mapped array
mapped.sort(function (a, b) {
// Loop through our properties
for (var i = 0; i < fields.length; i++) {
// Get our value (skip the first)
var field = fields[i],
x = a[field.name],
y = b[field.name];
// If our values are the same, go to the next compare
if (x === y)
continue;
// If we are using greater than
if (field.operator === '>') {
// Check that the value matches our expression
return y < field.expression ? -1 : 1;
}
// If we are using less than
if (field.operator === '<') {
// Check that the value matches our expression
return y > field.expression ? -1 : 1;
}
}
});
field.expression holds the range value. As you can see I am doing a loop around my fields and then trying to sort.

Your comparison function will need to check whether an item is in the range and then put that before an item outside of the range. If both are inside, or both are outside, they have to be compared as usual.
The following code does that:
var array = [2,55, 22, 6, 7];
var low = 6,
high = 23;
array.sort(function(a, b) {
return ((b >= low && b < high) - (a >= low && a < high)) || (a - b);
});

This function sorts your array and then pushes all numbers bigger than the given minimum value to the back of the array.
EDIT: Better performance by calling splice() only once.
var array = [2,55, 22, 6, 7];
function sortBetween(arr, min){
var sorted = arr.sort(function(a, b) {
return a - b;
});
var spliceIndex = sorted.length - 1;
while(sorted[spliceIndex] > min) {
spliceIndex--;
}
var tmp = sorted.splice(spliceIndex, sorted.length - 1 - spliceIndex);
tmp.forEach(function(el, i) {
sorted.unshift(el);
});
return sorted;
}
console.log(sortBetween(array, 6));
Result is, that your sorted array starts with your minimum number. There is no need to set the maximum, because you said:
the last 3 items can appear in any order to be honest, but the range I have sorted must appear first.

If an array element falls out of the range you care about, just replace it with an absurdly large value so it moves to the end of the list.
Because a and b have local scope, this won't affect the actual array, just the comparison.
array.sort(function (a, b) {
if (a<6 || a>23) a = Number.MAX_SAFE_INTEGER;
if (b<6 || b>23) b = Number.MAX_SAFE_INTEGER;
return b > a ? -1: 1;
});

Related

Finding the second lowest frequent element from an array of integers?

I'm looking to answer a coding challenge in JavaScript that I'm stuck on, here's the question:
Write a function which accepts an array of integers and returns an element of that array.
The function should determine the frequency of each element (how many times the element appears in the array) and whenever possible should return the element with the second-lowest frequency. Otherwise it should return the integer with the lowest frequency.
If there is more than one element satisfying the requirements then the second smallest one (according to value) should be returned.
Example outputs:
secondLowest( [4, 3, 1, 1, 2] ) === 1
secondLowest( [4, 3, 1, 1, 2, 2] ) === 2
secondLowest( [4, 3, 1, 2] ) === 2
This is what I've got so far although don't know how best to go about answering it after this:
function mode(array) {
if (array.length == 0) return null;
let modeMap = {};
let maxEl = array[0],
maxCount = 1;
for (let i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
I was determined to give a generic, parameterized function, where no number is hardcoded.
Your example involved two hardcoded values:
The second least frequency should be selected
In cases of ties, the second least value should be selected
The following code works like this:
Get the frequency of each input value
Group together all values with the same frequency.
Sort these grouped pairs by frequency and select the nth-lowest (in your case, n=2)
If the nth-lowest frequency has multiple pairs, sort these pairs by value, and select the mth-lowest pair (in your case, m=2)
Return the value of this final pair
The m and n parameters I refer to here are called freqInd and valInd in the code. Note that in order to select the second-lowest frequency, freqInd should be 1, not 2 (since 0 would select the lowest, and therefore 1 selects the second-lowest).
let lowestFreqVal = (freqInd, valInd, values) => {
// Calculate frequencies in a map
let f = new Map();
for (let v of values) f.set(v, (f.get(v) || 0) + 1);
// Group together all val/freq pairs with the same frequency
let ff = new Map();
for (let [ val, freq ] of f) ff.set(freq, (ff.get(freq) || []).concat([ val ]));
// Sort these groups by frequency
let byFreq = [ ...ff ].sort(([ freq1 ], [ freq2 ]) => freq1 - freq2);
// Here are all the items of the `freqInd`-th lowest frequency, sorted by value
// Note that `[1]` returns an array of integers at the frequency, whereas `[0]` would return the frequency itself
let lowestItems = byFreq[ Math.min(byFreq.length - 1, freqInd) ][1]
.sort((v1, v2) => v1 - v2);
// Return the `valInd`-th lowest value
return lowestItems[ Math.min(lowestItems.length - 1, valInd) ];
};
console.log('Some random examples:');
for (let i = 0; i < 10; i++) {
// An array of random length, full of random integers
let arr = [ ...new Array(3 + Math.floor(Math.random() * 5)) ]
.map(v => Math.floor(Math.random() * 4));
// Show the result of `lowestFreqVal` on this random Array
console.log(`lowestFreqVal(1, 1, ${JSON.stringify(arr)}) = ${lowestFreqVal(1, 1, arr)}`);
}
This is not an optimal solution, since it resorts to using sort. It's known that the problem of finding some nth-maximal value in a list can be implemented to have a better runtime than sort (and a significantly better runtime when n is a small value - we can see this intuitively because if n=0, a single pass (O(n)) does the trick).

Write a function to determine whether an array contains consecutive numbers for at least N numbers

I am trying to write a function to determine whether an array contains consecutive numbers for at least N numbers. For example, the input is [1,5,3,4] and 3, it turns true because the array has 3 consecutive numbers, which is [3,4,5]
Here this function requires sorting beforehand and it is not the most eloquent solution in my opinion. Can someone take a look and make some improvements on this?
function hasConsecutiveNums(array, N) {
if (array.length < N) return false;
if (N === 0) return true;
const sortedArray = array.slice().sort((a, b) => a - b);
let count = 0;
let prev = null;
for (const num of sortedArray) {
if (prev && num === prev + 1) {
count++;
} else {
count = 1;
}
if (count === N) return true;
prev = num;
}
return false;
}
console.log(hasConsecutiveNums([1, 4, 5, 6], 3)) // true
console.log(hasConsecutiveNums([1, 4, 5, 6], 4)) // false
Sorting the array first is not bad, but your program is broken by duplicates and returns false for ([4, 5, 5, 6], 3). That's easily fixed.
Sorting first takes O(N log N) time. You can do this in expected O(N) time, but the method is more complex and would probably only be faster for very large inputs.
It works like this:
Create a disjoint set data structure: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
Iterate through the array, mapping each unique integer you find to a set in that structure.
When you add a new integer, if sets exist for the preceding and following integers, then merge them with the new one.
When you're done, find the size of the largest set, which represents the largest sequence of consecutive numbers.
You could make some changes
initialize prev with undefined (null acts as number zero if you add a number), this allowes to
omit check with prev
move check for count inside of the first ifstatement and exit early if the wanted incremented count is found.
function hasConsecutiveNums(array, N) {
if (array.length < N) return false;
if (N === 0) return true;
const sortedArray = array.slice().sort((a, b) => a - b);
let count = 0;
let prev = undefined;
for (const num of sortedArray) {
if (num === prev + 1) {
if (++count === N) return true;
} else {
count = 1;
}
prev = num;
}
return false;
}
console.log(hasConsecutiveNums([1, 4, 5, 6], 3)) // true
console.log(hasConsecutiveNums([1, 4, 5, 6], 4)) // false

How to get the sum of all duplicates in an array?

I am trying to calculate the sum of all duplicates in an array. For example:
duplicate([1,1,2,3,3]) --> should return 8.
I have written the following function to calculate the sum of duplicates in an array using JavaScript. Currently it is returning an array with duplicates one less than what they are present in the array.
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
stack.push(arr[i])
}
}
return stack;
}
console.log(duplicate([1,2,1,2,2,3,3]))
This is returning [ 1, 2, 2, 3 ]
How do I get the correct array and calculate the correct sum? I have to use Object for that?
To make the logic easier, you might filter out the non-duplicates by checking whether their indexOf in the array is equal to their lastIndexOf in the array:
function duplicate(arr) {
const duplicates = arr.filter(elm => arr.indexOf(elm) !== arr.lastIndexOf(elm));
return duplicates.reduce((a, b) => a + b);
}
console.log(duplicate([1,1,2,3,3])); // --> should return 8.
console.log(duplicate([1,2,1,2,2,3,3]));
Initially create an object where the keys will be the integer and their value will be the number of occurrence. Then if the number of occurrence is more than 1 , multiply the number with number of occurrence.
function duplicate(arr) {
let dupVal = 0;
let k = arr.reduce((acc, curr, index) => {
if (acc[curr] === undefined) {
acc[curr] = 1
} else {
acc[curr] += 1;
}
return acc
}, {});
for (let keys in k) {
if (k[keys] > 1) {
dupVal += parseInt(keys, 10) * k[keys]
}
}
return dupVal;
}
console.log(duplicate([1, 2, 1, 2, 2, 3, 3]))
Try This one
const arr = [1,1,2,3,3]
let dup = arr.filter((value, index)=>{
// creating a copy of main array
let copyarr = [].concat(arr)
// removing present value
copyarr.splice(index,1)
// after removing present value, if you still
// get the value in copied array that means
// it has duplicates
if(copyarr.indexOf(value)>-1){
return true
}
return false
})
// now add it using reduce
let sum = dup.reduce((acc, value)=> acc+value,0)
console.log(sum)
Copy above code and paste into chrome devTool. You will get the answer.
The problem is that you are matching value with immediate next value in array, in array that is sorted already it will work, but not on unsorted one. So try to sort the array first and then run your code.
Edit :
Looks like sorting is added in code,
But another condition => if there is number that is repeated more than twice it should be handled and only appear once in stack, if that is required.
This will : console.log(duplicate([1,2,1,2,2,3,3]))
Result this : [1,2,3]
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
if(stack.length == 0 || (arr[i] != stack[stack.length-1])){
stack.push(arr[i])
}
}
}
return stack;
}
you can use JS Array.reduce method to accomplish your requirement in a shorter way
function sumDplicates(arr) {
return arr.reduce(function(tot, val, index, _arr) {
if (_arr.lastIndexOf(val) > index || _arr.indexOf(val) != index)
return tot + val;
return tot
}, 0)
}
console.log(sumDplicates([1, 1, 2, 3, 3]));
console.log(sumDplicates([1, 2, 1, 2, 2, 3, 3]));
You can pursue your original sorting approach with a slight modification:
if (arr[i] === arr[i + 1] || arr[i] === arr[i - 1])
That is, check if the previous or the next element in the sorted array is equal to the current element for it to qualify as a duplicate.
The following solution accomplishes this with filter and reduce:
function duplicate(array) {
return array
.sort((a, b) => a - b)
.filter((a, i, arr) => (arr[i] === arr[i + 1] || arr[i] === arr[i - 1]))
.reduce((a, b) => a + b, 0);
}
console.log(duplicate([1, 1, 2, 3, 3]));
console.log(duplicate([1, 2, 1, 2, 3, 3]));
Array.reduce() and Array.lastIndexOf() will simply solve your problem.
function sum(arr)
{
return arr.reduce(function(sum, item){
return arr.lastIndexOf(item)!==arr.indexOf(item) ? sum+=item : sum;
},0)
}
console.log(sum([1,1,2,3,3]));
console.log(sum([1,2,3,4]));
console.log(sum([1,2,2,3,4]));
console.log(sum([1,1,2,2,3,3,4,4]));
Though I don't know much about JavaScript, If I were you, I would have simply kept a temporary array, which copies all the duplicate variables and then use that array for sum.
Also, if you want to add the particular number as many times as it appears, I will suggest creating a table like the one in sparse matrices
and then referring to it during addition.
This logic, though not space efficient, is very easy to implement.
Here is an approach with a single Array.reduce and nothing else. No Array.indexOf or Array.lastIndexOf. Although it might not be as concise it does not traverse the array looking for indexes multiple times nor does any Array.filter:
const sumDubs = arr => arr.reduce((r,c) => {
if(r[c]) {
r[c] += 1
r.sum += r[c] > 2 ? (r[c]*c) - ((r[c]-1)*c) : r[c]*c
} else r[c] = 1
return r
}, { sum: 0 }).sum
console.log(sumDubs([1, 1, 2, 3, 3])) // 8
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3])) // 14
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3, 3, 1, 2, 4, 4])) // 28
The idea is to keep track of the on-going sum via a property in the accumulator of the Array.reduce and simply keep calculating the sum based on which number is duplicated and more importantly how many times.

Optimize- get third largest num in array

So, I was working on this challenge to return the third largest number in an array. I had got it worked out until I realized that I must account for repeat numbers. I handled this by adding 3 layers of for loops with variables i, j, and k. You'll see what I mean in the code. This is not terribly efficient or scalable.
My question is, how can I optimize this code? What other methods should I be using?
function thirdGreatest (arr) {
arr.sort(function(a, b) {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
} else {
return 0;
}
});
for ( var i = 0; i < arr.length; i++) {
for (var j = 1; j < arr.length; j++) {
for (var k = 2; k < arr.length; k++) {
if (arr[i] > arr[j]) {
if (arr[j] > arr[k]) {
return arr[k];
}
}
}
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Since you already sorted the array, it seems like you should be fine iterating over the list and keep track of the numbers you have already seen. When you have seen three different numbers, return the current one:
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
function thirdGreatest (arr) {
arr.sort(function(a, b) {
return b - a;
});
var seen = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== seen[0]) {
if (seen.length === 2) {
return arr[i];
}
seen.unshift(arr[i]);
}
}
}
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3
Note: You can simplify the sort callback to
arr.sort(function(a, b) {
return b - a;
});
// With arrow functions:
// arr.sort((a, b) => b - a);
The callback has to return a number that is larger, smaller or equal to 0, it doesn't have to be exactly -1 or 1.
A one-"line"r using Set to remove duplicates
Array.from(new Set(arr)).sort(function(a, b) {
return b - a;
})[2];
Set now has reasonable browser support
The optimal solution is to do this in a single pass O(n) time. You do not need to sort the array - doing so makes your solution at-least (n log n).
To do this in as single pass, you simply need three temporary variables: largest, secondLargest, thirdLargest. Just go through the array and update these values as necessary (i.e. when you replace largest it becomes second largest, etc...). Lastly, when you see duplicates (i.e. currentValue == secondLargest), just ignore them. They don't affect the outcome.
Don't forget to check for edge cases. You cannot provide an answer for [2, 2, 2, 2, 2] or [3, 2].
Try to think about what data structure you can use here. I suggest a set. Every time you add a nested loop your function gets exponentially slower.
Edited:
function thirdGreatest(arr) {
var s = Array.from(new Set(arr)).sort(function(a, b) {
return a - b;
})
return s[2] || s[1] || s[0] || null;
}
Working Example
We need to be able to handle:
[1,2,1,2] // 2
[1,1,1,1] // 1
[] // null
This assumes that you get an array passed in.
If you do not have a third largest number, you get the second.
If you do not have a second largest you get the first largest.
If you have no numbers you get null
If you want the 3rd largest or nothing, return s[2] || null
Many of the other answers require looping through the initial array multiple times. The following sorts and deduplicates at the same time. It's a little less terse, but is more performant.
const inputArray = [5,3,23,24,5,7,3,2,5,10,24,2,31,31,31];
const getThirdGreatest = inputArray => {
const sorted = [inputArray[0]]; // Add the first value from the input to sorted, for our for loop can be normalized.
let migrated = false;
let j;
for(let i = 1; i<inputArray.length; i++) { // Start at 1 to skip the first value in the input array
for(j=0; j<sorted.length; j++) {
if(sorted[j] < inputArray[i]) {
// If the input value is greater than that in the sorted array, add that value to the start of the sorted array
sorted.splice(j,0,inputArray[i]);
migrated = true;
break;
} else if(sorted[j] === inputArray[i]) {
// If the input value is a duplicate, ignore it
migrated = true;
break;
}
}
if(migrated === false) {
// If the input value wasn't addressed in the loop, add it to the end of the sorted array.
sorted[sorted.length] = inputArray[i];
} else {
migrated = false;
}
}
// Return the third greatest
return sorted[2];;
};
const start = performance.now();
getThirdGreatest(inputArray); // 23
const end = performance.now();
console.log('speed: ', end - start); // 0.1 - 0.2ms
One single iteration O(n) and very fast method of doing this is making your own Set like object. The advantageous point is making no comparisons at all when constructing our "sorted" list of "unique" elements which brings enormous efficiency. The difference is very noticeable when dealt with huge lists like in the lengths exceeding 1,000,000.
var arr = [5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31],
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null))),
third = sorted[sorted.length-3];
document.write(third);
If you think Object.keys might not return a sorted array (which i haven't yet seen not) then you can just sort it like it's done in the Set method.
Here i tried it for 1,000,000 item array and returns always with the correct result in around 45msecs. A 10,000,000 item array would take like ~450msec which is 50% less than other O(n) solutions listed under this question.
var arr = [],
sorted = [],
t0 = 0,
t1 = 0,
third = 0;
for (var i = 0; i<1000000; i++) arr[i] = Math.floor(Math.random()*100);
t0 = performance.now();
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null)));
third = sorted[sorted.length-3];
t1 = performance.now();
document.write(arr.length + " size array done in: " + (t1-t0) + "msecs and the third biggest item is " + third);

JavaScript - Special case of subset sum algorithm

From a given array of positive integers, I want to know if the sum of E elements from the array is equal to a given number N.
For example, given the array arr = [1, 2, 3, 4] , e = 3 and n = 9. It means if the sum of 3 elements in arr equals to 9. The result is true since 2 + 3 + 4 is equal to 9.
Another example with arr = [1, 2, 3, 4] , e = 2 and n = 7. It is true since 3 + 4 is equal to 7.
I'm trying to resolve it with recursion, but I'm stuck. My idea is to nest loops dynamically to walk through the elements to the array and compare them.
My attempt is this:
function subsetsum(arr, elements, n) {
loop(arr, elements, n, [], 0);
}
function loop(arr, elements, n, aux, index) {
if(aux.length != elements) {
aux[index] = arr.length - 1;
loop(arr, elements, n, aux, index + 1);
} else {
if ((elements - index + 1) < 0) {
return 0;
} else {
if (aux[elements - index + 1] > 0) {
aux[elements - index + 1]--;
loop(arr, elements, n, aux, index);
}
}
}
}
subsetsum([1, 2, 3, 4], 3, 9));
A related question is at Find the highest subset of an integer array whose sums add up to a given target. That can be modified to restrict the number of elements in the subset as follows:
// Find subset of a, of length e, that sums to n
function subset_sum(a, e, n) {
if (n < 0) return null; // Nothing adds up to a negative number
if (e === 0) return n === 0 ? [] : null; // Empty list is the solution for a target of 0
a = a.slice();
while (a.length) { // Try remaining values
var v = a.shift(); // Take next value
var s = subset_sum(a, e - 1, n - v); // Find solution recursively
if (s) return s.concat(v); // If solution, return
}
}
I've been playing around with this for a while and decided to use a short-cut, mainly the permutation code from this previous SO question.
My code uses basically uses the permutation code to create an array of all the possible permutations from the input array, then for each array (using map) grabs a slice corresponding to the number specified as amount, sums that slice and if it is the same as total returns true.
some then returns the final result as to whether there are any permutations that equals the total.
function checker(arr, amount, total) {
var add = function (a, b) { return a + b; }
return permutator(arr).map(function(arr) {
var ns = arr.slice(0, amount);
var sum = ns.reduce(add);
return sum === total;
}).some(Boolean);
}
checker([1, 2, 3, 4], 3, 9); // true
I've included two demos - 1) a demo showing this code, and 2) code that provides a more detailed breakdown: basically map returns an object containing the slice info, the sum totals and whether the condition has been met.
This is probably not what you're looking for because it's a bit long-winded, but it was certainly useful for me to investigate :)
Edit - alternatively here's a hacked version of that permutation code from the previous question that delivers the results and an array of matches:
function permutator(inputArr, amount, total) {
var results = [], out = [];
function permute(arr, memo) {
var cur, memo = memo || [];
var add = function (a, b) { return a + b; }
for (var i = 0; i < arr.length; i++) {
cur = arr.splice(i, 1);
if (arr.length === 0) {
results.push(memo.concat(cur));
}
var a = arr.slice();
// this is the change
var sum = memo.concat(cur).reduce(add);
if (memo.concat(cur).length === amount && sum === total) {
out.push(memo.concat(cur))
}
permute(a, memo.concat(cur));
arr.splice(i, 0, cur[0]);
}
return [results, out];
}
return permute(inputArr);
}
permutator([1,2,3,4], 3, 9);
DEMO
If I understand you correctly, the solution of this task must be simple like this:
function subsetsum(arr, countElements, sum) {
var length = arr.length-1;
var temp = 0;
var lastElement = length-countElements;
console.log(lastElement);
for (var i = length; i > lastElement; i--) {
temp = temp+arr[i];
console.log('Sum: '+temp);
}
if (temp === sum) {
console.log('True!');
} else {console.log('False!')}
};
subsetsum([1, 2, 3, 4], 2, 7);

Categories

Resources