I have a tricky question, at least for me, today. First of all I want to present you the code example which I want to acomplish:
var numbers= [1,2,3,4];
var newNumbers = values.map((v1, v2) => {
return v1+v2;
});
console.log(newNumbers ); //[wanted output: 3, 7]
The idea behind that is that I can map over an array but not with one item like usualy, this time I want to do this with pairs. Also I want to handle this little problem incase I have an odd amount of numbers:
var numbers= [1,2,3,4,5];
var newNumbers = values.map((v1, v2) => {
return v1*v2; //when v1 is 5 and v2 out of range, treat v2 as 0
});
console.log(newNumbers ); //[wanted output: 2, 12, 0]
Is it possible to handle this in JS or does anybody have an idea how I could map over an array in this special way?
.map is only for when the input array and output array items are one-to-one. Since you're looking to go from 4 items to 2, you'll need a different method.
A plain for loop works.
const numbers = [1, 2, 3, 4];
const output = [];
for (let i = 0; i < numbers.length; i += 2) {
output.push(numbers[i] + (numbers[i + 1] || 0));
}
console.log(output);
The || 0 at the end there will work for when the last number isn't paired with another.
This is not extremely practical but a possible way to solve it using ES6 iterators:
function sumPairs(arr) {
const result = [];
const iterator = arr.values(); //1
for (const x of iterator) { //2
const [y = 0] = iterator; //3 and 4
result.push(x+y);
}
return result;
}
console.log(sumPairs([1, 2, 3, 4]));
console.log(sumPairs([1, 2, 3, 4, 5]));
Array#values() gets an iterator over the values of an array.
for..of will go through the iterator. That is how it works by spec - even if you use for (const x of arr) internally the for..of will get the default iterator of arr which is the same as .values().
Using array destructuring also works on iterators by spec. The name is actually a bit misleading - it looks like an array because of the [ and ] and you can use it on arrays like const [a, b, c] = someArray.
However destructuring assignment using square brackets will always draw from an iterator. If it is already given an iterator, it is the same as calling iterator.next().value. In the case of const [y] = iterator it would draw a second value after x. So, each iteration of the loop advances through two of the items of the array.
The destructuring assignment allows using some syntax sugar - in this case specifying default values. With [y = 0] the second item drawn from the array will have a default if it happens to be undefined. The for..of will stop once there are no more items the iterator can produce.
if the array had an even number of items, then getting a y will be the last item, so the next iteration the loop ends:
if the array has an odd number of items, then x will be the last item of the iterator and y will get a value from an already exhausted iterator and thus the last iteration will just use the default for y.
function pairs(arr) {
const iterator = arr.values();
for (const x of iterator) {
const [y = 0] = iterator;
console.log(x, y);
}
}
pairs([1, 2]);
console.log("-----");
pairs([1, 2, 3]);
See also: Iteration protocols on MDN
The above approach can be generalised with a generator function that accepts any iterable and returns pairs of items until it is exhausted.
function* pairs(iterable) {
const iterator = iterable[Symbol.iterator]();
for (const x of iterator) {
const [y] = iterator;
yield [x, y];
}
}
The sumPairs function then just needs to consume those pairs by summing them and then adding them to an array:
function* pairs(iterable) {
const iterator = iterable[Symbol.iterator]();
for (const x of iterator) {
const [y] = iterator;
yield [x, y];
}
}
function sumPairs(arr) {
const result = [];
for (const [x, y = 0] of pairs(arr)) {
result.push(x+y);
}
return result;
}
console.log(sumPairs([1, 2, 3, 4]));
console.log(sumPairs([1, 2, 3, 4, 5]));
With a generator in place, the sumPairs can furrther be condensed to just an Array.from(), since it accepts any iterable as a first parameter and a mapping function for a second parameter.
const sumPairs = arr =>
Array.from(pairs(arr), ([x, y = 0]) => x+y);
function* pairs(iterable) {
const iterator = iterable[Symbol.iterator]();
for (const x of iterator) {
const [y] = iterator;
yield [x, y];
}
}
const sumPairs = arr =>
Array.from(pairs(arr), ([x, y = 0]) => x+y);
console.log(sumPairs([1, 2, 3, 4]));
console.log(sumPairs([1, 2, 3, 4, 5]));
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; }
How can I group the same numbers in an array within an array?
I have the following array of numbers:
var arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
My output should be
[[1,1,1,1],[2,2],[3,3,3],4,[5,5],6]
You could reduce the array and check if the predecessor has the same value and if not take either an array or the value, depending of the next element.
var array = [1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6],
grouped = array.reduce((r, v, i, a) => {
if (v === a[i - 1]) {
r[r.length - 1].push(v);
} else {
r.push(v === a[i + 1] ? [v] : v);
}
return r;
}, []);
console.log(grouped);
I recently came across this issue. The answers provided work well but I wanted a simpler approach without having to import from lodash or underscore. Here's the tl;dr (too long didn't read):
const groupedObj = arr.reduce(
(prev, current) => ({
...prev,
[current]: [...(prev[current] || []), current],
}),
{}
);
const groupedObjToArr = Object.values(groupedObj);
Explanation
We want to create an object where each unique number is an attribute or key in the object and it's value is an array of all the numbers that are the same. For the provided array:
const arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
our object should come out to be:
{
1: [1,1,1,1],
2: [2,2],
3: [3,3,3],
4: [4],
5: [5,5],
6: [6]
}
To do that, we will reduce the provided array (docs on .reduce method can be found on MDN by searching array reduce).
arr.reduce((previous, current, idx, array) => {}, {});
Quick Breakdown on Reduce
Reduce is a method on Array that takes two parameters: a callback and an initial value. Reduce provides four parameters to our callback: previous item, which at the start is the same as our initial value so in the case previous is {} on the first iteration of the reduce; current is the value in the array we our on in our iteration; index is the index of the current element; and, array is a shallow copy of the original array. For our purposes, we shouldn't need the index or array parameters.
As we iterate through our array, we want to set the number or element as a key in the object:
arr.reduce((previous, current) => {
return {...prev, [current]: []};
}, {});
This is what this would look like on the first couple of iterations:
// 1 (first element of arr)
// returns {...{}, [1]: []} => {1: []}
// 1 (second element of arr)
// returns {...{ 1: [] }, [1]: []} => {1: []} we have a duplicate key so the previous is overwritten
// 1 (third element of arr)
// returns {...{ 1: [] }, [1]: []} => {1: []} same thing here, we have a duplicate key so the previous is overwritten
// ...
// 2 (fifth element of arr)
// returns {...{ 1: [] }, [2]: []} => {1: [], 2: []} 2 is a unique key so we add it to our object
We have an object with all of our unique keys that match all of the numbers in our array. The next step is populate each "sub array" with all the elements that match that key.
arr.reduce((previous, current) => {
return {...prev, [current]: [current]}; // n.b. [current]: allows us to use the value of a variable as a key in an obj where the right side [current] is an array with an element current
}, {});
This doesn't accomplish what we need because the previous sub will be overwritten each iteration and not appended to. This simple modification fixes that:
arr.reduce((previous, current) => {
return {...prev, [current]: [...prev[current], current]};
}, {});
The prev[current] returns the array associated with the key that matches the current item. Spreading the array with ... allows us to append the current item to the existing array.
// 1 (second element of arr)
// returns {...{ 1: [1] }, [1]: [...[1], 1]} => {1: [1, 1]}
// 1 (third element of arr)
// returns {...{ 1: [1, 1] }, [1]: [...[1, 1], 1]} => {1: [1, 1, 1]}
// ...
// 2 (fifth element of arr)
// returns {...{ 1: [1,1,1,1] }, [2]: [...[], 2]} => {1: [1,1,1,1], 2: [2]}
Quickly we will find an issue. We can't index current of undefined. In other words, when we initially start our object is empty (remember the initial value for our reduce is {}). What we have to do is create a fallback for that case which can be done simply by adding || [] meaning or an empty array:
// let's assign the return of our reduce (our object) to a variable
const groupedObj = arr.reduce((prev, current) => {
return {...prev, [current]: [...prev[current] || [], current]};
}, {});
Finally, we can convert this object to an array by using the values method on the Object type: Object.values(groupedObj).
Assertion
Here's a quick little assertion test (not comprehensive):
const arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
const groupedObj = arr.reduce(
(prev, current) => ({
...prev,
[current]: [...(prev[current] || []), current],
}),
{}
);
const groupedObjToArr = Object.values(groupedObj);
const flattenGroup = groupedObjToArr.flat(); // takes sub arrays and flattens them into the "parent" array [1, [2, 3], 4, [5, 6]] => [1, 2, 3, 4, 5, 6]
const isSameLength = arr.length === flattenGroup.length;
const hasSameElements = arr.every((x) => flattenGroup.includes(x)); // is every element in arr in our flattened array
const assertion = isSameLength && hasSameElements;
console.log(assertion); //should be true
You Can use reduce array method for this.
const arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
const resultArr = arr.reduce((item, index) =>{
if (typeof item.last === 'undefined' || item.last !== index) {
item.last = index;
item.arr.push([]);
}
item.arr[item.arr.length - 1].push(index);
return item;
}, {arr: []}).arr;
console.log(resultArr);
Here is another way to do this with just a single Array.reduce:
var arr = [1,1,1,1,2,2,3,3,3,4,5,5,6]
const grp = arr.reduce((r,c,i,a) => {
r[c] = [...r[c] || [], c]
r[c] = (a[i+1] != c && r[c].length == 1) ? r[c][0] : r[c]
return r
}, {})
console.log(Object.values(grp))
The idea is to keep track of what is coming up next in the reduce and if it is different than the current item check the current accumulator array for that item and if it is equal to 1 get the first item of it.
You can use lodash as below to get a map of elements with related duplicates
_.groupBy([6, 4, 6,6,4,2])
with the output
Object {4: [4], 6: [6, 6]}
if you want to convert it to an array you can just get the values of the object
Object.values(_.groupBy([6, 4, 6,6,4,2]))
to have the output
[[4], [6, 6]]
You can convert array to string and use regex to matchs same characters and then use .map() to prepare target structure
var arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
var newArr = arr.join('').match(/(\w)\1*/g).map(function(val){
return val.length == 1 ? +val : val.split('').map(Number);
});
console.log(newArr);
arr.sort();
new_arr = [];
arr.reduce(function (r, current_item) {
if (current_item !== r) {
new_arr.push([]);
}
new_arr[new_arr.length - 1].push(current_item);
return current_item;
}, undefined);
How about this?
const arr = [1,1,1,1,2,2,3,3,3,4,5,5,6]
const hashMap = {}
for(const num of arr) {
if (hashMap[num] !== undefined) { // To handle (0)s If any
if (Array.isArray(hashMap[num])) {
hashMap[num].push(num)
} else {
hashMap[num] = [hashMap[num], num] // add older and new value.
}
} else {
hashMap[num] = num
}
}
const result = Object.values(hashMap)
console.log(result)
You can resolve this with lodash (https://lodash.com/):
var arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
var arr2 = _.groupBy(arr);
var result = Object.keys(arr2).map(k => {return arr2[k]} );
console.log(result)
Only JS:
var arr = [1,1,1,1,2,2,3,3,3,4,5,5,6];
const groupBy = (x,f)=>x.reduce((a,b)=>((a[f(b)]||=[]).push(b),a),{});
var arr2 = groupBy (arr, v => v);
var result = Object.keys(arr2).map(k => {return arr2[k]} );
console.log(result)
After reading the docs on map method,I still cannot get this to work.
I am trying to use map to get the average of every pair of numbers in an array.Please help me understand whats wrong.
function getAverage(num1,num2){return Math.ceil((num1+num2)/2)};
function a(input){ var b = input.map(getAverage(num1,num2)); return b; }
a([1,2,3,4]) //error num1 is not defined
//expected [2,4]
map projects a function to each element of a list/array, it simply "maps" a function over all the items.
[1, 2, 3].map(function (number) { return number + 1; });
// -> [2, 3, 4]
Therefor, first you need to have pairs of items in your "input" array, so it looks like this:
var numberPairs = [[1, 2], [3, 4]]
Until now, all you have are just single numbers but no pairs.
After conversion, you can use map like this:
numberPairs.map(function (pair) {
return Math.ceil((pair[0] + pair[1]) / 2);
});
This will give:
[2, 4]
as a result.
You can't calculate the average using a map. A mapping pass a function to each element and then returns an array with the same shape. That is not the case, you want to get a value from an array, and you can use the reduce method for that.
// adds two number
const adder = (a,b) => a + b;
// reduces the array adding all numbers and divides
// by the array length
const getAverage = (arr) => arr.reduce(adder)/arr.length;
// outputs 2.5
console.log(getAverage([1,2,3,4]))
You can use reduce() instead of map() to aggregate averages of every n values in the array:
const sum = array => array.reduce((a, b) => a + b, 0)
const getAverage = n => (averages, value, index, array) => index % n === 0
? [...averages, Math.ceil(sum(array.slice(index, index + n)) / n)]
: averages
const result = [1, 2, 3, 4].reduce(getAverage(2), [])
console.log(result)
I am trying to sort an object comparing with an array. So the loop will look for specific values on the array, until it finds one, and put those 3 elements at the beginning and the rest at the end.
I am unsure what is the best way to do this any ideas?
It is something like that:
var arr = [1, 3, 2,4,5,6, 2];
var arrSimilar = [1,2,5]
var testSortBy = _.sortBy(arr, function(arrSimilar){
// [1,2,5,3,4,6,2]
});
console.log(testSortBy); // [1,2,5,3,4,6,2]
You could use sorting with map and take the index of the value of similar array as priority sorting and then take the index of all other values as order.
Important is to delete a used value of the similar array, because it is now in use and has no meaning for further similar values. That means, same values are sorted to their original relative index.
var array = [1, 3, 2, 4, 5, 6, 2],
similar = [1, 2, 5],
result = array
.map(function (a, i) {
var priority = similar.indexOf(a);
delete similar[priority]; // delete value, but keep the index of other items
return { index: i, priority: (priority + 1) || Infinity };
})
.sort(function (a, b) {
return a.priority - b.priority || a.index - b.index;
})
.map(function (o) {
return array[o.index];
});
console.log(result); // [1, 2, 5, 3, 4, 6, 2]
You can do that in the following way
Suppose A[] is the original array and B is the priority Array
The answer would be (B intersection A) concat (A-B)
var arr = [1, 3, 2,4,5,6];
var arrSimilar = [1,2,5];
let bInterA = arrSimilar.filter((e) => arr.indexOf(e) != -1);
let aDiffb = arr.filter((e) => arrSimilar.indexOf(e) == -1);
console.log(bInterA.concat(aDiffb));