I want to return an array of indices of an array which has positive elements using pure functions in JS. EX:
pos([1,-4,0,5,2])
>>[0,2,3,4]
So, I tried:
arr.map(function (ele){
return arr.findIndex(ele => ele > 0);
})
But my output is :[1,1,1,1]
Can someone tell me what is wrong? I believe that my map function is correct but something is wrong with findIndex(). (Please don't tell me the code as it is a question of my assignment. Only need hints.)
map() isn't quite right because you won't have corresponding output for every element in the original array. reduce() is probably what you want in this case:
function pos(arr){
return arr.reduce((ret_arr, number, index) => {
if (number >= 0) ret_arr.push(index)
return ret_arr
}, [])
}
console.log(pos([1,-4,0,5,2]))
You could first map the index, if the value is positive or -1 and then filter the array for existing indices.
Array#map returns a new array with the index of the element or -1 if the element is smaller than zero.
Array#filter returns all values which are not -1. The short adding just one returns a truthy value which is a possible index.
function pos(array) {
return array // [1, -4, 0, 5, 2]
.map((v, i) => v < 0 ? -1 : i) // [0, -1, 2, 3, 4]
.filter(i => i + 1); // [0, 2, 3, 4]
}
console.log(pos([1, -4, 0, 5, 2]));
Your callback for mapping
function pos(arr) {
return arr.map(function (ele) {
return arr.findIndex(ele => ele > 0);
});
}
console.log(pos([1, -4, 0, 5, 2]));
takes no element, because the first ele variable is never used later. Then you return always the index of the first element of arr, which is 1. This value is greater as zero and the callback for find returns true abd find hands over the actual index.
Don't use findIndex. Basically you are looking for
function pos(arr) {
var res = [];
for (const [index, el] of arr.entries())
if (el > 0)
res.push(index)
return res;
}
If you want to do this using the higher-order array methods, you could do
function pos(arr) {
return arr.map((ele, index) => [ele, index])
.filter(([ele]) => ele > 0)
.map(([_, origIndex]) => origIndex);
}
#NinaScholz has a good approach as well, though I'd write it as
function pos(arr) {
return arr.map((ele, index) => ele > 0 ? index : null)
.filter(index => index !== null);
}
Try this:
[1,-4,0,5,2].reduce((acc,value,i) => value >= 0 ? [...acc,i] : acc,[]);
In any case where you're ding both filtering and ?something-else? reduce is always the best option.
For basic algorithms like this try returning a new array containing the previous values via spread.
const possitive = [];
const negative = [];
const array = [-1, -2, 0, 4, 1, 3, 2];
// Separate negative from possitive values
array.map(number =>
number >= 0 ? possitive.push(number) : negative.push(number)
);
// Sort accessing order
possitive.sort((a, b) => a - b);
negative.sort((a, b) => a - b);
// Join both arrays
const completed = possitive.concat(negative);
// Console log completed array
console.log(completed);
//Result
[0, 1, 2, 3, 4, -2, -1];
Related
function dropElements(arr, func) {
let output = arr.reduce((acc=[], elem) => {
if (func(elem)){
acc.push(arr.slice(arr.indexOf(elem)))
return acc
}
}, [])
return output
}
let tester = dropElements([1, 2, 3, 4,5,6,3,2,1], function(n) {return n >= 3;})
console.log(tester)
I want it to output [3,4,5,6,3,2,1]. But is printing copies of size decreasing arrays.
The statement of the problem: "Given the array arr, iterate through and remove each element starting from the first element (the 0 index) until the function func returns true when the iterated element is passed through it."
You can use Array#slice along with Array#findIndex to find the first index at which the callback function returns true.
function dropElements(arr, func) {
const idx = arr.findIndex(func);
return idx >= 0 ? arr.slice(idx) : [];
}
console.log(dropElements([1, 2, 3, 4, 5, 6, 3, 2, 1], n => n >= 3));
If you specifically want to use Array#reduce, you could use a variable to store whether or not the callback has returned true for any element so far.
function dropElements(arr, func) {
let found = false;
return arr.reduce((acc, elem) => {
found = found || func(elem);
if (found) acc.push(elem);
return acc;
}, []);
}
console.log(dropElements([1, 2, 3, 4, 5, 6, 3, 2, 1], n => n >= 3));
Use the shift method to remove the first element from the array until the array is empty or func(arr[0]) returns a truthy value. This method is inefficient as it could be, but it matches the problem statement. Particularly:
It removes each element singularly, rather than all at once.
It removes elements while iterating.
It operates on the array itself, rather than a copy.
function dropElements(arr, func) {
while (arr.length > 0 && !func(arr[0])) {
arr.shift();
}
return arr;
}
let tester = dropElements([1, 2, 3, 4,5,6,3,2,1], function(n) {return n >= 3;});
console.log(tester);
I am trying to understand why this part -- arr.splice(index, 1).push(0) -- of my code is not working.
why can't I splice and then push?
var moveZeros = function (arr) {
const newArray = arr.map((element, index) => element === 0 ? arr.splice(index, 1).push(element) : console.log(index, element))
}
Array.splice() returns an array of the elements that are removed from the array on which splice() was called on. Find documentation here:
Array.splice() documentation
You can achieve your goal by following code snippet:
let arr = [1, 0, 2, 0, 1, 0, 1, 0, 1, 0]
var moveZeros = function (arr) {
let zeroes = [];
let a = arr.reduce((finalArray, element) => {
element === 0 ? zeroes.push(element) : finalArray.push(element)
return finalArray;
}, []);
return [...a, ...zeroes];
}
console.log(moveZeros(arr));
splice returns an array of the removed elements. If you do
arr.splice(index, 1).push(0)
then you're pushing to the array of removed zeros (and that expression is subsequently discarded immediately afterwards).
You also shouldn't mutate an array while iterating over it - that'll make for very confusing behavior.
Consider filtering out zeros, while pushing to an array of zeros instead, and then combine the two arrays afterwards:
const moveZeros = (arr) => {
const zeros = [];
return arr
.filter((elm) => {
if (elm === 0) {
zeros.push(0);
return false;
}
return true;
})
.concat(zeros);
};
console.log(moveZeros([0, 1, 2, 3]));
console.log(moveZeros([3, 2, 1,0, 1, 2, 3]));
You could take a sorting instead and move all zeroes to the end. This approach rely on modern user agents which keep a stable sorting for equal (the onces whoes callback return zero) elements.
var array = [0, 1, 9, 2, 0, 0, 3, 5, 4, 2];
array.sort((a, b) => !a - !b);
console.log(array)
I need to take this array [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20] and return a new array which sorts all the duplicate numbers into their own nested array.
it should return [[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591].
I have messed around with for loops as well as the .map() method and can't seem to land on the solution. At this point I am almost just taking shots in the dark hoping something will work out. I am very new at javascript.
const cleaner = array1.map((num, index, array) => {
if (array1.indexOf(num) >= index) {
return num;
} else {
return array;
}
});
You can use a Set to collect the unique values then use Array.prototype.map() and Array.prototype.filter() to create the subarrays for each value.
const array = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
// defines a function that...
const arrange = array =>
// creates array of unique values
Array.from(new Set(array))
// sorts unique values in ascending order
.sort((a, b) => a - b)
// creates a subarray for each unique value
.map(key => array.filter(value => key === value))
// unwraps unit length subarrays
.map(subarray => subarray.length === 1 ? subarray[0] : subarray);
const cleaner = arrange(array);
console.log(JSON.stringify(cleaner));
This is not the most efficient approach, but it is considerably more readable (in my opinion) than procedural approaches using the reduce() method, and for an array of this length, the difference in execution time will be negligible.
You can do this using reduce, see the comments in the snippet :
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const result = arr.reduce((acc, curr) => {
// check if the element exists in the accumulator
const ndx = acc.findIndex(e => (Array.isArray(e) ? e.includes(curr) : e === curr));
if (ndx === -1) {
// if it doesn't exist, push it
acc.push(curr);
} else {
// if it exists, check if it's an array
if (Array.isArray(acc[ndx])) acc[ndx].push(curr); // if it is, push the current element
else acc[ndx] = [acc[ndx], curr]; // convert the current element in accumulator to an array with the previous and the new elements
}
return acc;
}, []);
console.log(result);
In order to do that, you need to perform a few operations:
Count occurrence of each number.
Sort non-repeating numbers.
Map it to have either number (non-repeating), or array of repeated numbers.
const array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
Step 1
Let’s count items. For that, I would create an object that will keep a track of each.
const counted = array.reduce(
(acc, current) => Object.assign(acc, { [current]: acc[current] ? acc[current] += 1 : 1 }),
{}
);
Using reduce we create an object, which will look like that:
{
'1': 5,
'2': 3,
'4': 1,
'5': 1,
// etc.
}
Step 2
Let's sort elements:
const sorted = Object.keys(counted)
.map(Number) // <-- we have to remember that object keys are strings
.sort((a, b) => a - b);
Step 3
Now that we have prepared object with counts and have them sorted, let’s put it together. If number in counted is 1 then we’ll insert value directly, otherwise we’ll transform it into array of values.
const final = sorted.map(
(number) => counted[number] == 1
? number
: Array.from({ length: counted[number] }, () => number)
);
You could take an object and use the implicit sorting.
var array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = Object.values(array.reduce((r, v) => {
if (v in r) r[v] = [].concat(r[v], v);
else r[v] = v;
return r;
}, {}));
console.log(result);
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.
This JavaScript code tries to get the indices of a given value 5 in a new array. Any idea how to do it in an elegant way. I cold use for loop but I was hoping to use map or reduce. thx.
console.log( [1, 2, 3, 5, 6, 5].map((y, i) => {
if (y === 5) return i
}))
// gives --> [undefined, undefined, undefined, 3, undefined, 5]
// expected [3,5]
Unfortunately map and reduce are not lazily-evaluated in JavaScript (i.e. they're not generators/iterators), but if you don't mind the double array allocation, you can do this:
var indices = [ 1, 2, 3, 5, 6, 5 ]
.map( ( e, i ) => e == 5 ? i : -1 )
.filter( e => e > -1 );
// indicies == [3,5]
Another approach, that's cheaper:
var indices = [];
[ 1, 2, 3, 5, 6, 5 ].forEach( (e, i) => e == 5 ? indices.push( i ) : null );
This takes advantage of the fact you can use a void expression inside the ?: ternary operator.
You can use reduce. The third argument of the callback is the index.
[1, 2, 3, 5, 6, 5].reduce((indexes, n, index) => {
if (n === 5) indexes.push(index)
return indexes
}, [])
You can either use map() or reduce().
reduce() will be able to do it in a single loop:
[1,2,3,4,5].reduce((result, num, index) => result.concat(num === 5 ? index : []), []);
Using concat() instead of push() is a tiny bit slower, but cleaner, and in reasonably small sets, the difference will be negligible.
map() will need filter() to remove extras:
[1,2,3,4,5].map((num, index) => num === 5 && index)
.filter(e => e !== false);
.filter(Boolean) is a clean, short-hand way of casting whatever value to a Boolean, which filter will then use to determine what it needs to do. num === 5 && index will be either false or the index. Another clean way to go through.
You can use reduce operator on your array as:
[1,2,3,5,6,5].reduce(function(a, e, i) {
if (e === 5)
a.push(i);
return a;
}, []);
You could run a map to test the value and write either a null (no match) or the position (match) then a filter to remove the nulls.
The map would look like this:
map( (value, index) => {
return value === 5 ? index : null;
})
Then you'd filter it like this:
filter( (value) => {
return value != null;
})
console.log( [1, 2, 3, 5, 6, 5].map((y, i) => {
if (y === 5) return i
}).filter((x) => {return /\d/.test(x);}));