I couldn't find an answer to this specific question on S.O.
Let's say I have an array of strings, or in this case, numbers:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
I'd like the output to be:
var output = [[1,1,1], [2], [3,3,3,3,3], [4], [5, 5, 5]];
I was hoping to use Lodash but most of that stuff tends to remove duplicates rather chunk them together into their own array. Maybe some kind of .map iterator?
The order of the output doesn't really matter so much. It just needs to chunk the duplicates into separate arrays that I'd like to keep.
You can use reduce to group the array elements into an object. Use Object.values to convert the object into an array.
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => {
(c[v] = c[v] || []).push(v);
return c;
}, {}));
console.log(result);
Shorter version:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => ((c[v] = c[v] || []).push(v), c), {}));
console.log(result);
You can do this with Array.reduce in a concise way like this:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = x.reduce((r,c) => (r[c] = [...(r[c] || []), c],r), {})
console.log(Object.values(result))
The exact same with lodash would be:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = _.values(_.groupBy(x))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Using _.values to extract the values of the grouping object and _.groupBy to get the actual groupings
Use Array#prototype#reduce to group them:
const x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let helperObj = {};
const res = x.reduce((acc, curr) => {
// If key is not present then add it
if (!helperObj[curr]) {
helperObj[curr] = curr;
acc.push([curr]);
}
// Else find the index and push it
else {
let index = acc.findIndex(x => x[0] === curr);
if (index !== -1) {
acc[index].push(curr);
}
}
return acc;
}, []);
console.log(res);
Since you're hoping to use Lodash, you might be interested in groupBy. It returns on object, but the _.values will give you the nested array:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let groups = _.values(_.groupBy(x))
console.log(groups)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Here's an imperative solution:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
x.sort();
var res = [];
for (const [i, n] of x.entries()) {
if (n !== x[i-1]) res.push([n]);
else res[res.length-1].push(n);
}
console.log(res);
Related
I'm trying to find the smallest common multiple from one array by comparing it to the values in the other by using % num === 0. The answer should be 6, but because each number in the first array is true at least once, they all get returned. How do I find the value that is only true and never false?
let arr = [1, 2, 3]
function test(arr) {
let x = [1, 2, 3, 4, 5, 6, 7, 8]
for (let n of arr) {
return x.filter(k => k % n === 0)
}
}
console.log(test(arr))
You need loop over the x array and return the first element that gets divided by every value in arr.
let arr = [1, 2, 3];
function test(arr) {
let x = [1, 2, 3, 4, 5, 6, 7, 8];
for (let n of x) {
if (arr.every((a) => n % a === 0)) {
return n;
}
}
}
console.log(test(arr));
You can also simply the solution using Array.prototype.find.
const
arr = [1, 2, 3],
test = (arr) =>
[1, 2, 3, 4, 5, 6, 7, 8].find((n) => arr.every((a) => n % a === 0));
console.log(test(arr));
Note: If x is not sorted, then you will have to sort it first.
const arr = [1, 2, 3],
test = (arr) =>
[8, 7, 6, 5, 4, 3, 2, 1]
.sort((a, b) => a - b)
.find((n) => arr.every((a) => n % a === 0));
console.log(test(arr));
Update based on OP's comment
You can use Array.prototype.filter and Array.prototype.some.
const arr = [1, 2, 3],
test = (arr) =>
[1, 2, 3, 4, 5, 6, 7, 8].filter((n) => arr.some((a) => n / a === 2));
console.log(test(arr));
If x is sorted can use find and every
let arr = [1, 2, 3]
let x = [1, 2, 3, 4, 5, 6, 7, 8, 12]
let res = x.find(n => arr.every(a => n%a === 0))
console.log(res)
if unsorted x
let arr = [1, 2, 3]
let x = [1, 12, 6, 4, 2, 7, 8]
let res = [...x].sort((a,b)=> a-b).find(n => arr.every(a => n%a === 0))
console.log(res)
Filter and intersect
let arr = [1, 2, 3]
let x = [1, 2, 3, 4, 5, 6, 7, 8]
function test(arr) {
let common = []
arr.forEach(n => common.push(x.filter(k => k % n === 0)))
return common.reduce((acc, cur) => acc.filter(e => cur.includes(e)));
}
console.log(test(arr))
Late to the party answer
You could solved this with one line of code using Array.reduce. Here we initialize it with an empty array and then use a ternary operator to append values that pass the remainder test. Note that we don't need to check for zero (c % n === 0) because we treat the result as a boolean.
x.reduce((p,c) => c % n ? p : p.concat([c]), [])
// TEST
const x = [1, 2, 3, 4, 5, 6, 7, 8];
[1, 2, 3].forEach(n => {
console.log(
"n =" + n,
x.reduce((p,c) => c % n ? p : p.concat([c]), [])
);
});
I would like to interleave two arrays, BUT only return pairs when a certain condition is met. As an example:
first_array = [1, 2, 3, 4, 5, 6, 7, 8];
second_array = [, , , , 1, , 0, 1];
I need to return ONLY pairs where array-2 is non-null, in other words, the output I need is:
interleaved = [5, 1, 7, 0, 8, 1];
I have an interleave function that works:
function send_values() {
let interleaved = [];
for (let i = 0; i < first_array.length; i++) {
interleaved.push(first_array[i], second_array[i]);
}
}
...but the output is, obviously:
interleaved = [1, , 2, , 3, , 4, , 5, 1, 6, , 7, 0, 8, 1];
...which is not what I need. Suggestions?
You could iterate the sparse array and take only the values with the values at the same index from array one.
var array1 = [1, 2, 3, 4, 5, 6, 7, 8],
array2 = [, , , , 1, , 0, 1],
result = array2.reduce((r, v, i) => r.concat(array1[i], v), []);
console.log(result);
Here's a generic functional solution:
pairs = (a, b) => a.map((_, i) => [a[i], b[i]])
flat = a => [].concat(...a)
valid = x => x === 0 || Boolean(x)
array_1 = [1, 2, 3, 4, 5, 6, 7, 8];
array_2 = [ , , , , 1, , 0, 1];
result = flat(
pairs(array_1, array_2)
.filter(x => x.every(valid))
)
console.log(result)
Works both ways, that is, it doesn't matter which array contains the null values. Also, you can redefine valid if there are other things to exclude.
As a bonus, if you replace pairs with zip, you can make it work for any number of arrays.
So I have an array of ids something like this:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
I need a function that will be called like mergeArray(arr, 3), and it should return comma separated values with maximum of 3 elements like this:
const newArr = ['1,2,3', '4,5,6', '7,8,9', '10,11'];
How can I do this? If possible with ES6 functions for simpler code.
slice your array into 3 lengths arrays and directly join them
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
const mergeArray = (arr, size) => {
let res = [];
for (i = 0; i < arr.length; i += size) {
res.push(arr.slice(i, i + size).join(','));
}
return res;
}
console.log(mergeArray(arr, 3));
You can split() the array into the specific size and join() them before pushing into the resulting array:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
var i, j, newArr=[], size = 3;
for (i=0,j=arr.length; i<j; i+=size) {
newArr.push(arr.slice(i, i+size).join());
}
console.log(newArr);
One of the ways to do it is with Array.prototype.reduce and Array.prototype.map:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
function mergeArray(arr, n) {
return arr
.reduce((all, el, i) => {
const ind = Math.floor(i/n);
all[ind] = [...all[ind] || [], el]
return all;
},[])
.map(a => a.join(','))
}
console.log(mergeArray(arr, 3));
You could join the array and match the wanted parts with a regular expression.
var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
result = data.join(',').match(/\d+(,\d+(,\d+)?)?/g)
console.log(result);
var arr = ['verdana', 'Verdana', 2, 4, 2, 8, 7, 3, 6];
result = Array.from(new Set(arr));
console.log(arr);
console.log(result);
i want to remove any duplicates case-insensitive
so the expected result should be
['Verdana', 2, 4, 8, 7, 3, 6]
but it doesn't seem to work...
JavaScript comparator is case sensitive. For strings you may need to clean up the data first:
var arr = ['verdana', 'Verdana', 2, 4, 2, 8, 7, 3, 6]
.map(x => typeof x === 'string' ? x.toLowerCase() : x);
result = Array.from(new Set(arr));
// produces ["verdana", 2, 4, 8, 7, 3, 6];
Alternatively, you may use reduce() with a custom nested comparing logic. The implementation below compares the items ignoring the case, but for "equal" strings it picks the first occurrence, regardless what its "casing" is:
'verdana', 'Moma', 'MOMA', 'Verdana', 2, 4, 2, 8, 7, 3, 6]
.reduce((result, element) => {
var normalize = x => typeof x === 'string' ? x.toLowerCase() : x;
var normalizedElement = normalize(element);
if (result.every(otherElement => normalize(otherElement) !== normalizedElement))
result.push(element);
return result;
}, []);
// Produces ["verdana", "Moma", 2, 4, 8, 7, 3, 6]
var arr = ['verdana', 'Verdana', 2, 4, 2, 8, 7, 3, 6];
function getUniqueValuesWithCase(arr, caseSensitive){
let temp = [];
return [...new Set(caseSensitive ? arr : arr.filter(x => {
let _x = typeof x === 'string' ? x.toLowerCase() : x;
if(temp.indexOf(_x) === -1){
temp.push(_x)
return x;
}
}))];
}
getUniqueValuesWithCase(arr, false); // ["verdana", 2, 4, 8, 7, 3, 6]
getUniqueValuesWithCase(arr, true); // ["verdana", "Verdana", 2, 4, 8, 7, 3, 6]
You can use Set after converting the string elements to uppercase.Here ... is spread operator
var arr = ['verdana', 'Verdana', 2, 4, 2, 8, 7, 3, 6];
var result = arr.map(function(item) {
return typeof item === "string" ? item.toString().toUpperCase() : item
})
result = [...new Set(result)];
console.log(result);
I want to map a sequence to another sequence such as
map (0,1,2,3,4) + (1,2,3,4,5) -> (1,3,5,7,9)
How to do that in lazy.js or underscore ?
Thanks!
You can use _.zip and _.map, like this
var _ = require("underscore");
function sum(numbers) {
var result = 0;
for (var i = 0; i < numbers.length; i += 1) {
result += numbers[i];
}
return result;
}
console.log(_.map(_.zip([0, 1, 2, 3, 4], [1, 2, 3, 4, 5]), sum))
// [ 1, 3, 5, 7, 9 ]
Since only two numbers are going to be there, always, you can simplify that like this
var result = _.chain([0, 1, 2, 3, 4])
.zip([1, 2, 3, 4, 5])
.map(function(numbers) {
return numbers[0] + numbers[1];
})
.value();
console.log(result);
You can make it a little more generic and clean, like this
function sum(numbers) {
return numbers.reduce(function(result, current) {
return result + current;
}, 0);
}
var result = _.chain([0, 1, 2, 3, 4])
.zip([1, 2, 3, 4, 5])
.map(sum)
.value();
Or even simpler, like in the first answer
console.log(_.map(_.zip([0, 1, 2, 3, 4], [1, 2, 3, 4, 5]), sum));
Using underscore, #thefortheye's solution works well, here's a similar solution just using lazy.js instead;
> var Lazy = require('lazy.js');
> var addParts = function(x) { return Lazy(x).sum(); }
> Lazy([0,1,2,3,4]).zip([1,2,3,4,5]).map(addParts).toArray()
[ 1, 3, 5, 7, 9 ]
The above example can be achieved in the following way using underscore:
_.map([0, 1, 2, 3, 4], function(n, i) {
return n + i + 1
}) // This returns [1, 3, 5, 7, 9]
Here's a link to the API docs: Underscore#map