Hello I have an array as follows:
[[1,1],[2,8],[3,12],[4,13],[5,23],[6,30],[7,41],[8,44],[9,50]]
So lets say it is the format of [[x,y]]
As you can see the above array, the first element inside inner arrays goes from 1,2...6 which is x, and the second element of inner arrays are 1,8,12,13,23 and 30 which is y.
This is a cumulative array of y and I want to convert it into non-cumulative but with a twist.
I want to convert y to non-cumulative array by getting the difference from the value of y at last 3rd value of x
Therefore I want the final result to be as follows:
[[1,1],[2,8],[3,0],[4,1],[5,11],[6,0],[7,11],[8,14],[9,0]]
I have tried a fiddle here:
https://jsfiddle.net/gxytx6ka/1/
So far I have been able to get the difference between two array elements by some help of Dekel (stackoverflow guy) as you can see in the above fiddle:
$(document).ready(function() {
var old_arr = [ [1, 1], [2, 8], [3, 12], [4, 13], [5, 23], [6, 30], [7, 41], [8, 44], [9, 50] ];
var new_arr = old_arr.reduce(function(a, b, c, d) {
if (a.length) {
a.push([d[c][0], d[c][1] - d[c - 1][1]])
} else {
a = [b]
}
return a;
}, []);
console.log(new_arr);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
which outputs:
[[1,1],[2,7],[3,4],[4,1],[5,10],[6,7],[7,11],[8,3],[9,6]]
but I want:
[[1,1],[2,8],[3,0],[4,1],[5,11],[6,0],[7,11],[8,14],[9,0]]
which I am not able to get.
I tried using a%3==0 in if condition, but not working...
Basically I want the final output in an efficient way.
Why reduce? A simple loop does the job perfectly fine:
let a = [[1,1],[2,8],[3,12],[4,13],[5,23],[6,30],[7,41],[8,44],[9,50]];
let out = [],
n = 0,
lastY = 0;
for (let [x, y] of a) {
if (++n % 3 == 0)
lastY = y;
out.push([x, y - lastY]);
}
console.log(JSON.stringify(out))
You could use Array#map, which returns simply an array with the result.
var array = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]],
result = array.map(function (a, i) {
(i + 1) % 3 || (this.last = a[1]);
return [a[0], a[1] - this.last];
}, { last: 0 });
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6
var array = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]],
result = array.map((l => ([x, y], i) => (++i % 3 || (l = y), [x, y - l]))(0));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It is possible to do this with reduce — You just need to figure out when to zero-out every third value and when to find the difference between the current value and it's previous (third) value.
Naming parameters with more intelligible names also helps out.
var old_arr = [ [1, 1], [2, 8], [3, 12], [4, 13], [5, 23], [6, 30], [7, 41], [8, 44], [9, 50] ];
var new_arr = old_arr.reduce(function(result, value, index, arr) {
if (index % 3 == 2) {
return result.concat([[value[0], 0]]); // Set every third value to 0.
} else {
var prevIndex = index - (index % 3) - 1;
var prevVal = prevIndex > -1 ? arr[prevIndex][1] : 0;
return result.concat([[value[0], Math.abs(prevVal - value[1])]]);
}
}, []);
console.log(JSON.stringify(new_arr)); // [[1,1],[2,8],[3,0],[4,1],[5,10],[6,0],[7,11],[8,3],[9,0]]
With reduce you can do it like this (also using ES6 arrow function and spread operator):
var arr = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]];
var result = arr.reduce( ([arr, v], [x, y], i) =>
[[...arr,[x, i%3==2 ? 0 : y-v]], i%3==2 ? y : v], [[],0])[0];
console.log(result);
Explanation
reduce is called with a double initial value (a pair):
[[],0]
The first element is the array that will accumulate into the final result, and the second value is the current value to which subsequent y values will be offset.
These two values are captured by the argument [arr, v] which is a destructuring assignment (ES6). The reduce callback will always return a new pair like that, with extended arr and (potentially) updated v.
The callback function also takes the current [x, y] pair from the original array. Again this is a destructuring assignment, so you have direct access to x and y variables. The third argument i is the current, zero-based index in the original array.
As said, the callback returns a pair. The first element in that pair is constructed as follows:
[...arr, [x, i%3==2 ? 0 : y-v]]
The ...arr notation spreads the previously collected result elements as a list, and one pair is added to that list: [x, i%3==2 ? 0 : y-v]. The x is just reproduced from the original [x, y], but the y follows the requested logic with the ternary operator: if we are at an index that has a remainder of 2 when divided by 3, then the y value should be 0. Otherwise, it should be offset against the previously set value v.
The second element in the pair, must be the new value for v:
i%3==2 ? y : v
Again, according to the requested logic, v remains unchanged when the remainder is not 2, but otherwise it is set to the current value of y.
So reduce will thus accumulate/update these two values in each iteration, and finally return a pair, of which only the first has our interest, which is why there is [0] at the end.
Notes
As you seemed to be looking for a reduce implementation, that is what I went with, but whenever your output array has just as many elements as your input array (like is the case here) you can also consider to use map as a good alternative (See answer that NinaScholz posted).
If you are open for a less functional programming way, you can also choose to use a for loop and maybe gain a bit of performance.
Related
i have a problem with reduce.
I want to add the rest of the arrays from the first array in the array.
array = [[a,b],[c,d],[e,f]]
the order to come is: (a-b)+(c-d)+(e-f), but my code not work :((
my code is:
const sumAndSubtract = (numberInNestedArray) => {
return numberInNestedArray.reduce((acc, cur, ind, arr) => {
const firstSubtract = arr[0][0] - arr[0][1];
const otherSubtract = acc[ind] - cur[ind];
console.log(firstSubtract);
console.log(otherSubtract);
return firstSubtract + otherSubtract;
})
}
console.log(sumAndSubtract([
[10, 0],
[5, 7],
[6, 9]
]))
expect result: 10 + (-2)+(-3)
Use reduce as follows, where a is the accumulating sum, and b and c are the atomic values retrieved from each pair:
let result = [[10, 0], [5, 7], [6, 9]].reduce((a, [b, c]) => a+b-c, 0);
console.log(result);
In your attempt, the following is not right:
The expression arr[0][0] - arr[0][1]. This only looks at the first pair in the array, and does so in each iteration. Instead, you should be doing cur[0] - cur[1].
The expression acc[ind] - cur[ind]: ind is the index in the outer array, which takes values 0, 1 and 2 for the example input. acc[ind] is a pair, not a number and cur[ind] will at some point be an out of range reference, as cur only has two values.
The call to reduce should get the initial value argument, because without it, the first pair will be the initial value of acc, which you don't want: you want a number. So pass 0 as initial value argument to reduce.
What you call otherSubtract is already given as acc, so you actually don't need this variable.
The shortest way to do it will be:
numberInNestedArray.reduce((acc, cur) => acc + (cur[0] - cur[1]), 0)
You could map the subtractions and add the values later.
const
add = (a, b) => a + b,
subtract = (a, b) => a - b,
sumAndSubtract = array => array
.map(a => a.reduce(subtract))
.reduce(add);
console.log(sumAndSubtract([[10, 0], [5, 7], [6, 9]]));
This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 4 years ago.
I have an array with random numbers, from 1 to 6.
How can I know if I have in this array two, three or four same numbers ? And what is that number ? Or if I have two numbers that they appear twice ?
array = [1, 3, 5, 5, 6];
x = array[0];
repetitions = 0;
for (let i = 0; i < 5; i++) {
const y = array[i];
if (x === y) {
repetitions++;
}
}
Should I do something like this for each element of array ?
Thank you!
This is a simple solution that can be provided using filter;
var array = [1,2, 3,4,4,4,4,4,4,4, 5, 5, 6,6,6];
var duplicate = [];
var newArray = array.filter((v, i) =>{
if(array.indexOf(v) == i){
return v
}else{
duplicate.indexOf(v) == -1 ? duplicate.push(v):'';
}
})
console.log(`new array ${newArray}`);
console.log(`Duplicate values array ${duplicate}`);
There are some constraints you should clarify. For example, this code would returns the first number in the array that is duplicated:
let duplicated = [...array] // makes a copy of the array
.sort() // since `sort` mutate the original
.find((item, index, arr) => value === arr[index + 1]);
console.log(duplicated) // 5, in your case
If there is no number duplicated, then it will return undefined.
However, maybe that is not what you want. Considering this array: [1, 3, 6, 5, 5, 6, 6, 6]. Even if there is also 6 duplicated, you will always return 5.
If you want just to know if there is at least a number duplicated, the code above will works. If you want to know all the duplicated number, and in which order they appear first in the original array, you need something different. For example:
let array = [1, 3, 6, 5, 5, 6, 6, 6];
let occurrences = array
.reduce((acc, value) => (acc[value]=-~acc[value], acc),{});
console.log(occurrences); // {"1": 1, "3": 1, "5": 2, "6": 4}
At this point you can decided what do you want to do with this data, e.g. you can filter out the numbers that appears in the array just once:
console.log(
Object.fromEntries(
Object.entries(occurrences).filter(([num, count]) => count > 1)
)
); // {"5": 2, "6": 4}
etc.
UPDATE (see comments):
Object.fromEntries is a method recently introduced, in case having a filtered object with all the occurrences is the goal, in this context could be easily replaced by a function like this:
const fromEntries = entries =>
entries.reduce((acc, [key, value]) => (acc[key] = value, acc), {});
You check if the value in array exists more than 2 times by compare indexOf and lastIndexOf the value
function duplicates(arr){
let result = []
arr.forEach(item => {
if(arr.indexOf(item) !== arr.lastIndexOf(item)){
result.push(item)
}
})
return [... new Set(result)];
}
console.log(duplicates([1,2,3,5,5,6,6,6]));
You could take a Map and count all occurences of the values.
The result is an array where the first element is the value and the second is the count of the value.
var array = [1, 3, 5, 5, 6],
counts = Array.from(array.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map));
console.log(counts);
.as-console-wrapper { max-height: 100% !important; top: 0; }
this may seem like a given but I want an answer where I only use what I believe to be functional programming... so no for() loops. Say I have a multi dimensional array which contains arrays of numbers and numbers only! Something like this:
var numberArray = [[1,5,12],[1,-1], [5,11,45,-3],[0,1,2] ... ];
Now instead of having nested loops I would like to use .forEach and / or .reduce to get the sum of all numbers in numberArray, however I would also like to perform an action so that any negative numbers are omitted or given the value 0 when doing the addition. I thought something like this would work:
numberArray.reduce(function(a, b) {
a.concat(b);
}, []);
but I am not sure how to perform the action to set negative numbers to 0 (or just omit them) and then get the sum...
Use Array#reduce() twice.
var numberArray = [[1, 5, 12], [1, -1], [5, 11, 45, -3], [0, 1, 2]],
sum = numberArray.reduce(function (r, a) {
return a.reduce(function (s, b) {
return b > 0 ? s + b : s;
}, r);
}, 0);
document.write(sum);
You may need to do reduce twice.
Try this
numberArray.reduce(function(prev,current){return prev.concat(current)}).reduce(function(prev,current){return prev+current});
DEMO
var numberArray = [[1,5,12],[1,-1], [5,11,45,-3],[0,1,2]];
var output = numberArray.reduce(function(prev,current){return prev.concat(current)}).reduce(function(prev,current){return prev+current});
document.body.innerHTML += output;
Try to use an nested reduce at this context,
var numberArray = [[1,5,12],[1,-1], [5,11,45,-3],[0,1,2]]
var x = numberArray.reduce(function(a, b) {
return a + b.reduce(function(c,d){
return c + ((d > 0) ? d : 0);
}, 0);
}, 0);
console.log(x); //206
I am trying to separate the negative & positive elements of an array in Javascript, such that afterwards first come all negative elements and then the positive elements, each in the original order.
Example:
Input array: [1,2,-3,-2,4]
Output array: [-3,-2,1,2,4]
Input array: [3,2,-1,0,-4,3,6,-7,-6]
Output array: [-1,-4,-7,-6,3,2,0,3,6]
I can do it using a temporary array with use of push() method, but how to do this without using a temporary array in that array only?
Use sort()
var res = [1, 2, -3, -2, 4].sort(function(a, b) {
return a - b;
});
// or just use, var res = [1, 2, -3, -2, 4].sort();
document.write(JSON.stringify(res));
For getting sorted as you like you need to add custom sorting conditions.
Update :
In your case sort() will not preserve position of same valued items, so instead you can use filter() and concat(). Using filter() get negative , zero and positive numbers in different array after that concatenate it whatever order you want using concat().
var res = [3, 4, -6, 0, -8, -1, 3, -6, 1, -8, -6, -1];
//get positive negative values
var neg = res.filter(function(v) {
return v < 0;
}),
// get positive values
pos = res.filter(function(v) {
return v > 0;
}),
// get zeros
zero = res.filter(function(v) {
return v == 0;
});
// concat result arrays
res = neg.concat(zero, pos);
document.write(JSON.stringify(res));
Same method , without using any additional variable
var res = [3, 4, -6, 0, -8, -1, 3, -6, 1, -8, -6, -1];
res = res.filter(function(v) {
return v < 0;
}).concat(res.filter(function(v) {
return v == 0;
}), res.filter(function(v) {
return v > 0;
}));
document.write(JSON.stringify(res));
When sorting by a numeric value, the basic form of the function to pass to sort is
function comparator(a, b) { return func(a) - func(b); }
Here func is a function which takes the element to be sorted, and returns the sort index to use.
To sort by "sign" (negative vs. positive), use Math.sign, which gives the sign of the element:
function sign_comparator(a, b) { return Math.sign(a) - Math.sign(b); }
array.sort(sign_comparator)
This will sort negative numbers first and positive numbers last, otherwise leaving their order unchanged (but see important note below for some browsers which may not leave the order unchanged).
a = [3,2,-1,0,-4,3,6,-7,-6]
a.sort(sign_comparator)
< [-1, -4, -7, -6, 0, 3, 2, 3, 6]
Math.sign is ES6. For platforms such as IE which do not support it, write it yourself:
function sign(x) { return x < 0 ? -1 : x > 0 ? +1 : 0; }
If you want to write your code a bit more semantically, define a generic function to create a comparator function as
function make_numeric_comparator(func) {
return function(a, b) { return func(a) - func(b); };
}
Now you can write your sort as
a.sort(make_numeric_comparator(Math.sign))
Important note on stability
In some cases, as the OP helpfully pointed out, the original order is not always preserved. This behavior is known as the stability of the sort. In simple terms, does the sort preserve the original order of pairs of items for which the sort function returns 0? It turns out that Chrome's sort is not stable, at least in some cases, which is what you are seeing. On the other hand, FF sort is stable. For details, see this SO question and this V8 issue https://code.google.com/p/v8/issues/detail?id=90. Of course, we want our sort to be stable in all browsers. So does that mean this approach will not work?
No, but it means we have to do a workaround. Here is a stable sort function:
function stable_sort(array, sortfunc) {
function _sortfunc(a, b) { return sortfunc(array[a], array[b]) || a - b; }
return array.map((e, i) => i) . sort(_sortfunc) . map(i => array[i]);
}
> stable_sort([3, 4, -6, 0, -8, -1, 3, -6, 1, -8, -6, -1], sign_comparator)
< [-6, -8, -1, -6, -8, -6, -1, 0, 3, 4, 3, 1]
What this is doing is creating a parallel array of indexes, from 1 to array.length-1, with array.map((e, i) => i). It sorts those indexes with a special sort function which calls the original sort function, but if that function returns 0 (sort in same place), it imposes the ordering of the indexes. After the array of indexes is sorted, it then uses that to look up into the original array to create the result (with the map(i => array[i])).
This may be too much work, so you may prefer another solution. On the other hand, you may want stable sorting in other contexts as well, and so if you have the stable_sort function defined, this approach would still be more straightforward than filtering out the numbers with each sign and recombining them.
for the precise requirement of seperating positive and negative number
var t = [-1,-2,-3,5,6,1]
var positiveArr = [];
var negativeArr = [];
t.forEach(function(item){
if(item<0){
negativeArr.push(item);
}
else{
positiveArr.push(item)
})
console.log(positiveArr) // output [5, 6, 1]
console.log(negativeArr) // output [-1, -2, -3]
I have an Array of integers in javascript, [5,10,15,20,25,30,35]
when given a number x, how can I find the element in the array that is closest to that number?
If the number is over a value, but less than halfway to the next number, I would choose the smaller value, if it were over halfway to the next number, I would choose the higher number.
For example 7 would return 5, but 8 would return 10. How can I accomplish this? Any help or tips would be appreciated. I have searched and cannot find a solution. I'm sure this is sort of common.
Probably the easiest thing to do is sort based on distance from the reference value x, and then take the first item.
The built-in Array.prototype.sort() can take a comparison function which will be called for pairs of values from the array. Then the key is simply to pass in a comparison function which compares the two values based on their distance from the reference value x.
let x = 8;
let array = [5, 10, 15, 20, 25, 30, 35];
let closest = array.sort( (a, b) => Math.abs(x - a) - Math.abs(x - b) )[0];
See this simple demo.
function getClosest(array, target) {
var tuples = _.map(array, function(val) {
return [val, Math.abs(val - target)];
});
return _.reduce(tuples, function(memo, val) {
return (memo[1] < val[1]) ? memo : val;
}, [-1, 999])[0];
}
If using a functional approach is applicable then you can map the set to tuples of (value, distance) then reduce that set of tuples to the tuple with the smallest distance. We return the value in that tuple.
To explain the useage of _.map. You map all the values in your array to new values and the function will return the array of new values. In this case an array of tuples.
To explain the useage of _.reduce. You reduce the array to a single value. You pass in an array and a memo. The memo is your "running counter" as you move through the array. In this case we check whether the current tuple is closer then the memo and if so make it the memo. We then return the memo at the end.
The code snippet above relies on underscore.js to remove the nitty gritty of functional style javascript
Your example list is sorted. If this is always the case, then binary search for your number. If you don't find the exact number, make the binary search end off by checking the two numbers around where the number would be and return the closest. Be careful with edge cases where all numbers are greater or are all smaller than the target number
If the list isn't always sorted, then go through the list keeping track of the largest number <= the target number and the smallest number >= the target number. Return the one that's closest to the target.
In either solution, you'll need to decide which side to favour if for example you're searching for 2 in [1, 3].
Create a temporary array of the same size as your original array, and populate it with the differences between your x and the array element.
For example, let the temporary array be temp[], and your original array be a[]:
temp[i]=Math.abs(x-a[i]);
Then, return the index of the minimum value in temp[] to the user.
Assuming the array is sorted, step through each adjacent pair of integers in the array. For each pair (say "5 and 10" or "20 and 25"), test if x is in between them, and if so, return whichever one is closer to x (with a bias towards the lower one).
You would also need a special case for when x is less than the first number (return the first number) or greater than the last number (return the last number).
If the array is not sorted, sort it first.
I created my own function since i could not find any that meets my requeriments.
function closest_number(quantities, number, closest_factor)
{
if (closest_factor == 'ceil')
{
quantities.sort(function(a, b)
{
return a - b
}
);
for (var i = 0; i < quantities.length; i++)
{
if (quantities[i] >= number)
{
return quantities[i];
}
last_value = quantities[i];
}
return last_value;
}
else if (closest_factor == 'floor')
{
quantities.sort(function(a, b)
{
return a - b
}
);
min_value = quantities[0];
for (var i = 0; i < quantities.length; i++)
{
if (number == quantities[i])
{
return number;
}
else if (quantities[i] < number)
{
min_value = quantities[i];
}
else if(quantities[i] > number)
{
return min_value;
}
}
return min_value;
}
else
{
return false;
}
};
Since Array.reduce is a reality for so long (even IE9 supports it), the problem is easily solvable with it. This way, no need to sort the array first (no array mutation at all):
var numbers = [20, 25, 30, 35, 5, 10, 15], x = 7;
var output = numbers.reduce(function (prev, curr) {
return Math.abs(curr - x) < Math.abs(prev - x) ? curr : prev
});
console.log(output);
You can go further and solve it with only one line of ES6 (ECMAScript 2015) syntax, by using an arrow function (but with no IE support in this case):
const numbers = [20, 25, 30, 35, 5, 10, 15], x = 7;
const output = numbers.reduce((prev, curr) => Math.abs(curr - x) < Math.abs(prev - x) ? curr : prev);
console.log(output);
Of course, for flexibility and reusability, it's easy to make it as a function:
const closest = (array, goal) => array.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
console.log(closest([20, 25, 30, 35, 5, 10, 15], 7));
console.log(closest([20, 25, 30, 35, 5, 10, 15], 8));
console.log(closest([1, 5, 7], -5));
console.log(closest([1, 5, 7], 4));
console.log(closest([1, 5, 7], 20));