Count total of number array with ignoring overlap number [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last year.
Improve this question
I have this kind of problem and trying to solve it by using Javascript/Go. Given this array of number set, I would like to find the sum of number. The calculation should ignore the overlap and consider to count it as only once.
const nums = [[10, 26], [43, 60], [24,31], [40,50], [13, 19]]
It would be something like following if translated into the picture.
The result should 41
The rules are
Overlap set of number (pink area) should be count once.
Count total sum of green area.
Total for both.
Any help will be appreciated.

Here's an one-liner solution using javascript (Assuming the correct answer is 41 instead of 42).
The idea is to iterate all interval numbers and put them in a single array, then trim all the duplicates using Set. The time complexity is not optimal but it's short enough.
const nums = [[10, 26], [43, 60], [24, 31], [40, 50], [13, 19]];
const total = new Set(nums.reduce((acc, [from, to]) =>
[...acc, ...Array.from({ length: to - from }, (_, i) => i + from)], [])).size;
console.log(total);
Not sure how to do it with go but it's just a proposal.

You could sort the pairs and reduce by checking the second value and then add the deltas for getting the sum.
const
nums = [[10, 26], [43, 60], [24, 31], [40, 50], [13, 19]],
result = nums
.sort((a, b) => a[0] - b[0] || a[1] - b[1])
.reduce((r, [...a]) => {
const last = r[r.length - 1];
if (last && last[1] >= a[0]) last[1] = Math.max(last[1], a[1]);
else r.push(a);
return r;
}, [])
.reduce((s, [l, r]) => s + r - l, 0);
console.log(result)

Here's my version:
const getCoverage = arr => arr
.reduce((results, el) => {
if (!results.length) {
return [el];
}
let running = true, i = 0;
while(running && i < results.length) {
if (el.some(n => n >= results[i][0] && n <= results[i][1])) {
results[i] = [
Math.min(el[0], results[i][0]),
Math.max(el[1], results[i][1])
];
running = false;
}
i++;
}
if (running) {
results.push(el);
}
return results;
}, [])
.reduce((total, el) => el[1] - el[0] + total, 0);
console.log(
getCoverage([[10, 26], [43, 60], [24,31], [40,50], [13, 19]])
);
The first reducer merges overlapping (and adjacent) intervals and the second one adds up the diffs from the resulting merged ones.

You can spread the values of given 2-D array in an array and sort both of them.
Find the sum of the differences between the edge values of non-overlapping arrays and the sum of difference of values within overlapping area and the result will be the difference between the two.
const nums = [[10, 26], [43, 60], [24, 31], [40, 50], [13, 19]]
let arr = []
//spread given 2-D array
nums.forEach(item => arr=[...arr,...item]);
// Sort both arrays
arr.sort();
nums.sort();
let previtem, sum = 0;
let min , max ;
let count = 0;
//Loop to find sum of difference between non-overlapping values
nums.forEach((item,index) => {
if (index === 0) {
min = nums[0][0], max = nums[0][1]
}
else if (!(item[0]>min && item[0]<max) && !(item[1]>min && item[1]<max))
{
flag = 1;
count = count + (item[0] - max);
min = item[0];
max = item[1];
console.log(item,count);
}
else if (item[0]>min && item[0]<max && !(item[1]>min && item[1]<max)) {
max = item[1];
}
})
// loop to find sum of difference of unique values within overlapping area
arr.forEach(item => {
if (previtem && item !== previtem) {
sum=sum+(item-previtem)
}
previtem = item;
})
//Print the result
console.log(sum - count);

Related

Find the sum of the first n num [duplicate]

This question already has answers here:
Javascript - Counting array elements by reduce method until specific value occurs doesn't give a correct output
(4 answers)
Closed 2 years ago.
I have an array with numbers. Find the sum of the first N elements to the first zero. Task must be completed with
Example: - summarize the first 3 elements, because next is the element with the number 0.
You could do this as follows:
let arr = [10, 20, 3, 0, 16, 35, 1];
let sum = arr.slice(0, arr.indexOf(0)).reduce((sum, v) => sum + v);
console.log(sum);
I first extract the numbers in front of the first zero using Array.slice(). Then I use Array.reduce() to compute the sum of these numbers.
You can not break the iteration of reduce(). You can use a flag variable based on which you can add value to sum.
Try the following way:
let arr = [10, 20, 3, 0, 16, 35, 1];
let flag = true;
let sumNum = arr.reduce((sum, elem) => {
if (elem == 0) {
flag = false;
};
if(flag) return sum + elem;
else return sum + 0;
}, 0)
console.log(sumNum)
You can utilize an external variable stopCounting to flag if the counting should continue or not.
let arr = [10, 20, 3, 0, 16, 35, 1];
let stopCounting = false;
let sumNum = arr.reduce((sum, elem) => {
if (elem == 0) {
stopCounting = true;
}
return stopCounting ? sum : sum + elem;
}, 0);
console.log(sumNum);
You could iterate with a short circuit and check the value and add the value if not zero.
This approach does not search for zero in advance and needs only a single loop in the worst case.
var array = [10, 20, 3, 0, 16, 35, 1],
sum = 0;
array.every(v => (sum += v, v));
console.log(sum);
const arr = [10, 20, 3, 0, 16, 35, 1];
const zeroIndex = arr.indexOf(0);
const total = arr.slice(0, zeroIndex > 0 ? zeroIndex : arr.length)
.reduce(sum, n => sum+n, 0);
console.log(total);

How to filter an array based on a range and then take an additional sample either side of result

I am looking for a simple solution to the following example:
let rangeOfInterest = [25 , 44];
let input = [10, 20, 30, 40, 50, 60];
I'm interested in all values that are greater than 25 and less than 44 (inclusive). The rangeOfInterest may be completely inside or completely outside the input values, other examples include [85, 95] or [0, 100].
output1 = [30, 40];
If a value exists either side of this output then take that sample as well;
finalOutput = [20, 30, 40, 50];
I can achieve the above by applying a filter to the array then finding the index of the first and last elements of the result and extracting the additional samples based on that (if they exist). Is there a cleaner way of achieving this that doesn't involve ~20 lines of code?
Note: samples will be floats, used ints for a simpler example.
You can find the index of the first item that is larger than the minimum range, and the index of the first item that is larger than the maximum range, and slice according to the indexes (minIndex - 1, maxIndex + 1).
const fn = ([min, max], arr) => {
const startIndex = arr.findIndex(n => n > min); // find the index of the first item that is larger than min
const endIndex = arr.findIndex(n => n > max); // find the index of the first item that is smaller than max
return arr.slice(
startIndex > 1 ? startIndex - 1 : 0,
endIndex === -1 ? arr.length : endIndex + 1
);
};
const input = [10, 20, 30, 40, 50, 60];
console.log(fn([25 , 44], input));
console.log(fn([25 , 65], input));
console.log(fn([-25 , 65], input));
Try this:
let input = [10, 20, 30, 40, 50, 60];
let rangeOfInterest = [25 , 44];
let newOutput = input.filter(function(number) {
return Math.min(...rangeOfInterest) <= number && Math.max(...rangeOfInterest) >= number;
});
newOutput.push( Math.floor(Math.min(...rangeOfInterest) / 10) * 10 ); //adding 20
newOutput.push( Math.floor(Math.max(...rangeOfInterest) / 10) * 10 ); // adding 40
console.log(newOutput)
let roi = [25 , 44]; //simplifying range of interest variable name
let arr = [10, 20, 30, 40, 50, 60];
function inRange(val1,val2){//assuming val1<val2 always
return (num)=>{
return num>=val1 && num<=val2;
}
}
let firstInd=0;
let lastInd=0;
var resultArr = arr.filter((val,ind) => {
let match = inRange(roi[0],roi[1])(val);
if(!firstInd){
firstInd=match?ind:0;
}
lastInd = match&&(ind>lastInd)?ind:lastInd;
return match;
});
lastInd<arr.length?resultArr.push(arr[lastInd+1]):'';
firstInd>0?resultArr.splice(0,0,arr[firstInd-1]):'';
console.log(resultArr);

Sorting using two arrays

There are two arrays:
The first one contain numbers, and the second one contains "weight" of the first array values.
It works like this:
arr1 = [56,65,100,89,180,90];
"Weight" of the numbers are calculated in this way:
56 = 5+6 = 11;
65 = 6+5 = 11;
100 = 1+0+0 = 1; and so on..
So, arr2 = [11,11,1,17,9,9];
My question is how can I sort the values of the arr1 according to values of arr2?
I tried to modify simple bubble sort for this problem, but nothing changed.
function bubble(arr1, arr2) {
var len = arr1.length;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - i - 1; j++) {
if (arr2[j] > arr2[j + 1]) {
var temp = arr1[j];
arr1[j] = arr1[j + 1];
arr1[j + 1] = temp;
}
}
}
return arr1;
}
arr1 = [56, 65, 100, 89, 180, 90];
arr2 = [11, 11, 1, 17, 9, 9];
console.log(bubble(arr1, arr2));
I expect the output of the bubble function to be [100,180,90,56,65,89].
This is why:
FirstArray - [56,65,100,89,180,90] - arr1
"Weight of the values"[11,11, 1, 17, 9, 9 ] - arr2
Output [100,180,90,56,65,89]
[1, 9, 9 ,11,11,17]
You can just calculate the weight on the fly while sorting:
const arr = [56,65,100,89,180,90];
arr.sort( (a,b) =>
(a + '').split( '' ).reduce( (sum,x) => sum + +x, 0 ) -
(b + '').split( '' ).reduce( (sum,x) => sum + +x, 0 )
);
console.log( arr );
To achieve expected result, use below option of sorrting arr1 with logic mentioned in question
Sort arr1 bycomparing sum of digits
Convert number to string and split it to array
Use reduce sum all the digits and compare with the array elements
var arr1 = [56, 65, 100, 89, 180, 90];
var arr2 = [11, 11, 1, 17, 9, 9];
console.log(
arr1.sort(
(a, b) =>
a
.toString()
.split("")
.reduce((acc, v) => parseInt(acc) + parseInt(v)) -
b
.toString()
.split("")
.reduce((acc, v) => parseInt(acc) + parseInt(v))
)
);
codepen - https://codepen.io/nagasai/pen/WNeoaEv?editors=1010

javascript - find subset that gives maximum sum under a certain limit (subset sum )

I have an array with some integer values, and I need to get a subset of them that gives me the maximum sum that is inferior to a given value.
So let's say I have this array:
[40, 138, 29, 450]
I would like to get a subset of this array that maximize the sum but is inferior to a limit given by the user, let's say 250. In this case it should return [139, 40, 29].
I had a look at this question and related answer, and tried to use the code given, but I didn't understand it very well. Anyway I've tried it, setting the min to 0 and the max to the limit given, but it keeps returning me "5" that is not correct, since the limit is like 300 and the numbers in my array are all over 50.
I couldn't find anything that could help me, so I'm asking if anyone could give me some code or pseudocode to understand how to do this.
Basically you could either add the element at the index to a temporary array or not. Then check if the index reaches the lenght of the array or if the sum is greater than the wanted sum, then either check the sum and add the temp array to the result set, or not.
Proceed until all indices are visited.
function getCombinations(array, sum) {
function add(a, b) { return a + b; }
function fork(i, t) {
var r = (result[0] || []).reduce(add, 0),
s = t.reduce(add, 0);
if (i === array.length || s > sum) {
if (s <= sum && t.length && r <= s) {
if (r < s) {
result = [];
}
result.push(t);
}
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
console.log(getCombinations([40, 138, 29, 450], 250));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A fast and compact solution:
function maxSum(input, limit) {
const sums = {};
let max = 0;
const collectSums = (n, i, values) => {
for (; i < input.length; i++) {
const sum = n + input[i];
if (sum <= limit) {
values.push(input[i]);
if (sum >= max && values.length > 1) {
max = sum;
sums[max] = values.slice(); // https://jsperf.com/copying-an-array
}
collectSums(sum, i + 1, values);
}
}
values.pop();
};
collectSums(0, 0, []);
return sums[max] || [];
}
Apart from the necessary iterations of the input this solution tries to keep complexity low by not using costly array operations. Only a found subset has to be copied to keep track of possible combinations. Still, there are probably more clever solutions possible to improve performance.
The method will return the last found combination, this means that two input lists with the same values in different order might yield different results:
maxSum([1, 4, 200, 5], 205) == [200, 5];
maxSum([5, 200, 1, 4], 205) == [200, 1, 4];
If you want all possible combinations replace this line:
sums[max] = values.slice(); // https://jsperf.com/copying-an-array
with this:
sums[max] = sums[max] || [];
sums[max].push(values.slice());
All combinations are then returned:
maxSum([1, 4, 200, 5], 205) == [[1, 4, 200], [200, 5]];
But note that this will always return an array of arrays, even when there is only one possibility:
maxSum([40, 138, 29, 450], 250) == [[40, 138, 29]];
Here's a brute force solution. First we get every possible combination of values from the original array, take their sum, and see which of those gets us the highest value without overflowing the given maximum.
var ary = [40, 138, 29, 450];
// Function to construct a power set. A power set is just the set of
// all possible subsets of some given set.
function makePowerSet(ary) {
powerSet = [];
for (let ps = 1; ps <= Math.pow(2, ary.length); ps++) {
subset = [];
for (let i = 0; i < ary.length; i++) {
if (ps & Math.pow(2, i)) subset.push(ary[i]);
}
powerSet.push(subset);
}
return powerSet;
}
// Function to calculate the sum of an array.
function getSum(ary) {
return ary.reduce((sum, cur) => {
return sum + cur;
}, 0);
}
function getSubsetBelow(val, ary) {
let bestSoFar;
let bestSoFarSum = 0;
for (let subset of makePowerSet(ary)) {
const sum = getSum(subset);
if (sum > val) continue;
if (sum > bestSoFarSum) {
bestSoFar = subset;
bestSoFarSum = sum;
}
}
console.log("Got a best sum value of", bestSoFarSum, "with the subset", bestSoFar);
}
getSubsetBelow(250, ary)
This seems very similar to the knapsack problem, which is NP-hard, so I don't know if you'll ever be able to find an efficient algorithm for this. However, there are definitely a few optimizations that can be made to what I've written here, for example, any element of the array already greater than the limit can't be part of the solution (easy way to eliminate 450).
#Find a maximum sum of a compact subsequence of array elements.
import sys
def solution(A):
max_ending = max_slice = -sys.maxsize
if(len(A)==1):
return A[0]
else:
for a in A:
max_ending =max(a,max_ending + a)
max_slice = max(max_slice, max_ending)
return max_slice

Merge arrays with overlapping values

Im using Node.js. (...and underscore.js)
Consider this data structure
var numbers = [
[10, 20]
[30, 40]
[40, 50]
[45, 70]
... //Possibly more arrays (always contains two numbers)
]
numbers contain arrays that always contain number pairs. Think of these number pairs as "start" and "end". I want a function that takes numbers as argument, and loop trough its content, and if the "start" number of a pair overlap the "end" number of previous pair, these arrays is merged into one. For example this:
var numbers = [
[10, 20]
[19, 40]
[40, 60]
[70, 80]
]
Becomes this:
var numbers = [
[10, 60] // First, second and third array is merged because of overlapping .
[70, 80]
]
Actually, I already have written a function for this that works fine, but feels a bit clunky.
I'm curious if some javascript wizard can dazzle me with a super elegant solution =).
Create an empty "result" array. Loop over the ranges array and either change the last item of the result or add the current range to it.
function merge(ranges) {
var result = [], last;
ranges.forEach(function (r) {
if (!last || r[0] > last[1])
result.push(last = r);
else if (r[1] > last[1])
last[1] = r[1];
});
return result;
}
r = [[10, 20], [19, 40], [40, 60], [70, 80]];
document.write(JSON.stringify(merge(r)));
This assumes that the source array is sorted, if it's not always the case, sort it before merging:
ranges.sort(function(a, b) { return a[0]-b[0] || a[1]-b[1] });
I created a function which does what you want:
function merge(arr) {
// copy and sort the array
var result = arr.slice().sort(function(a, b) {
return a[0] > b[0];
}),
i = 0;
while(i < result.length - 1) {
var current = result[i],
next = result[i+1];
// check if there is an overlapping
if(current[1] >= next[0]) {
current[1] = Math.max(current[1], next[1]);
// remove next
result.splice(i+1, 1);
} else {
// move to next
i++;
}
}
return result;
};
This function can be used this way:
var mergedNumbers = merge(numbers);
DEMO
As #Brett said, this might be a better fit for Code Review (just be sure to include your current implementation). If you post there, put a reference to it here somewhere and I'll move my answer.
Assuming that your numbers array is already sorted correctly, this function should do what you want:
function combine(numbers) {
return numbers.reduce(function(combined, next) {
if (!combined.length || combined[combined.length-1][1] < next[0]) combined.push(next);
else {
var prev = combined.pop();
combined.push([prev[0], Math.max(prev[1], next[1])]);
}
return combined;
}, []);
}
var n = [[10, 20], [19, 40], [40, 60], [70, 80], [75, 76]];
var r = combine(n);
document.write('<pre>' + JSON.stringify(r) + '</pre>');
This "reduces" the original array to the new one using the following logic in the reduce function:
If this is the first pass or the last item does not overlap the current item, push the current item on to the combined array.
Otherwise:
pop the last item off the combined array.
push the combination of the last item and the current item on to the combined array.
Simple concise JavaScript solution:
Algo
Sort the intervals by the start index in ascending order.
If the current interval overlap with the previous one, update the previous interval accordingly.
Otherwise, if the current start value > the previous end value), we put the interval in the result.
Implement code
var merge = (intervals) => {
intervals.sort((a, b) => a[0] - b[0]);
const merged = [intervals[0]];
for (let i = 1; i < intervals.length; i++) {
const [start, end] = intervals[i];
let prev = merged[merged.length - 1];
if (prev[1] >= start) {
prev[1] = Math.max(prev[1], end);
} else merged.push(intervals[i]);
}
return merged;
};
console.log(merge([[10, 20], [19, 40], [40, 60], [70, 80]]));
let arr = [
[1, 3],
[2, 6],
[5, 10],
[15, 18],
[18, 6],
];
const mergeoverlapping = (arr) => {
if (arr.length < 2) return intervals;
arr.sort((a, b) => a[0] - b[0]);
let top = 0;
let down = arr.length - 1;
let left = 0;
let temp = [];
let right = arr[0].length - 1;
let result = [];
while (top + 1 <= down) {
if (arr[top][right] >= arr[top + 1][left]) {
arr[top + 1][left] = arr[top][left];
temp = [arr[top + 1][left], arr[top + 1][right]];
} else {
result.push(temp);
temp = arr[top + 1];
}
top++;
}
result.push(temp);
console.log(result);
};
console.log(mergeoverlapping(arr));
Expanding on accepted solution to provide more readability and a common use-case which is working with sets of integers where we want [[0,21],[22,42]] => [[0,42]]`:
const arr = [[21,21],[-21,1],[21,42],[5,10],[11,21]].sort((a, b) => a[0] - b[0]);
print(merge(arr));
print(merge(arr, true));
function merge(ranges, integers) { // range must be sorted by 1st element
let prev = ranges[0];
const result = [prev];
for (let i = 1; i < ranges.length; i++) {
const next = ranges[i];
if (next[0] > prev[1] + (integers ? 1 : 0)) {
result.push((prev = next));
continue;
}
if (next[1] > prev[1]) prev[1] = next[1];
}
return result;
}
function print(value) {
console.info(JSON.stringify(value))
}
Previous solutions are for closed intervals/ranges (where boundaries are included). This would be the approach for open intervals/ranges (boundaries not included):
const arr = [[21,21],[-21,1],[21,42],[5,10],[11,21]].filter(([a,b]) => a !== b).sort((a, b) => a[0] - b[0]); // 21 is not included
print(merge(arr));
function merge(ranges) { // range must be sorted by 1st element
let prev = ranges[0];
const result = [prev];
for (let i = 1; i < ranges.length; i++) {
const next = ranges[i];
if (next[0] >= prev[1]) { // >= instead of >
result.push((prev = next));
continue;
}
if (next[1] > prev[1]) prev[1] = next[1];
}
return result;
}
function print(value) {
console.info(JSON.stringify(value))
}

Categories

Resources