not removing duplicates using lodash difference - javascript

Using lodash, is it possible to remove one array from another while avoiding removing duplicates?
I'm currently using _.difference
// this returns [4]
_.difference([1, 1, 1, 2, 2, 2, 3, 4], [1, 2, 3])
// I want it to return [1, 1, 2, 2, 4]

This is how i would do it by pure JS
var arr1 = [1, 1, 1, 2, 2, 2, 3, 4],
arr2 = [1, 2, 3],
result = arr2.reduce((p,c) => {var idx = p.indexOf(c);
return idx === -1 ? p : (p.splice(idx,1),p)}, arr1);
console.log(result);

Based on comment from #hindmost, I used a loop.
var tempArray = [1,1,1,2,2,2,3,3,1,2]
_.each([1, 2, 3], function(value) {
tempArray.splice(tempArray.indexOf(value), 1);
});

Yes, It will return 4 Because _.difference Returns the new array of filtered values.I tried java script solution. Hope it will help you.
function keepDuplicate(array1, array2){
var occur;
var indexes = [];
_.each(array2, function(value){
_.each(array1, function(ar1value, index){
if(value === ar1value){
occur = index;
}
});
indexes.push(occur);
});
_.each(indexes, function(remove, index){
if(index === 0 ){
array1.splice(remove, 1);
}
else{
remove = remove-index;
array1.splice(remove,1);
}
});
return array1;
}
keepDuplicate([1, 1, 1, 2, 2, 2, 3, 4], [1, 2, 3])
It will Return [1, 1, 2, 2, 4]

Related

How can I check it an array contains all elements from another array, including count, in JavaScript?

I have two arrays:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
Using arr1 as the baseline, arr2 does not match because it contains three 1's whereas arr1 only has two. arr3, however should return true because it has elements from arr1.
I tried
if(_.difference(arr2, arr1).length === 0)
But this does not take into account the number of occurrences
You could count all value from the first array and iterate the second with every and return early if a value is not given or zero, otherwise decrement the count and go on.
function check(a, b) {
var count = a.reduce((o, v) => (o[v] = (o[v] || 0) + 1, o), {});
return b.every(v => count[v] && count[v]--);
}
var arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3];
console.log(check(arr1, arr2)); // false
console.log(check(arr1, arr3)); // true
You can try to loop through second array and compare t against main array if value found make main array cell to false and set flag as true
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function checkArray(compareMe, toThis){
var flag = false;
for(var i = 0; i < toThis.length; i++){
flag = false;
for(var j = 0; j < compareMe.length; j++){
if(compareMe[j] == toThis[i]){
compareMe[j] = false;
flag = true;
break;
}
}
}
return flag;
}
console.log(checkArray(arr1, arr2));
console.log(checkArray(arr1, arr3));
Try this solution:
const arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3],
arr4 = [1, 2, 3, 1, 2, 3, 4, 1];
function containsFn(src, trg) {
const srcCp = src.slice();
for(let i = 0; i < trg.length; i++) {
const index = srcCp.indexOf(trg[i]);
if(index > - 1) {
srcCp.splice(index, 1);
} else {
return false;
}
}
return true;
}
console.log(containsFn(arr1, arr2));
console.log(containsFn(arr1, arr3));
console.log(containsFn(arr1, arr4));
Looks like I'm already late to the party but this would be a recursive solution:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function findAllIn(find, search) {
if (find.length == 0) {
return true;
}
i = search.indexOf(find[0]);
if (i == -1) {
return false;
}
return findAllIn(find.slice(1,find.length), search.slice(0,i).concat(search.slice(i+1, search.length)));
}
console.log(findAllIn(arr2, arr1)); // false
console.log(findAllIn(arr3, arr1)); // true
This should do the trick
Not efficient but I think it is easy to understand
const count = (x, xs) =>
xs.reduce((y, xi) => y + (xi === x ? 1 : 0), 0)
const isInclusive = (xs, ys) =>
xs.every((xi) => count(xi, xs) >= count(xi, ys))
const arr1 = [1, 2, 3, 1, 2, 3, 4]
const arr2 = [1, 3, 1, 1]
const arr3 = [1, 1, 2, 2, 3]
console.log(isInclusive(arr1, arr2))
console.log(isInclusive(arr1, arr3))
Based on this answer: https://stackoverflow.com/a/4026828/3838031
You can do this:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
console.log((arr1.diff(arr2).length === 0) ? "True" : "False");
console.log((arr1.diff(arr3).length === 0) ? "True" : "False");
console.log((arr2.diff(arr1).length === 0) ? "True" : "False");
console.log((arr2.diff(arr3).length === 0) ? "True" : "False");
console.log((arr3.diff(arr1).length === 0) ? "True" : "False");
console.log((arr3.diff(arr2).length === 0) ? "True" : "False");

Javascript - Put array items, including their duplicates, into a new array

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);

Get item what's not on the 2nd, 3rd arrays - JS

I am trying to create a function that will get the items that cannot be seen on the 2nd or 3rd and upcoming arrays passed within the function.
Right now my function gets only the similar items. How can I make it get the difference (w/c are the items that doesn't exist to the 2nd and 3rd and proceeding arrays.
const callM = function(arrays) {
arrays = Array.prototype.slice.call(arguments);
let result = [];
for(let i = 1; i < arrays.length; i++){
for(let x = 0; x < arrays[i].length; x++){
if(arrays[0].includes(arrays[i][x])){
result.push(arrays[i][x]);
}
}
}
return result;
};
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The logic right now is a bit off as it gets the opposite. How do i fix this?
Also is there a way to do this using Higher Order functions such as reduce or filter?
Thanks!
I'd think about this differently. As the difference between two sets: array 0 and array 1...n
To get array 0, just shift it off the top
const arr0 = arrays.shift()
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
This removes the first array from arrays
Next we combine the remaining arrays
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
Ref: http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/
Unneeded, handled by includes as mentioned by #Phil
Next filter duplicates from arrN by comparing with itself
const unique = arrN.filter(function(elem, index, self) {
return index == self.indexOf(elem);
})
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Next filter with includes to find the difference (or union)
const diff = arr0.filter(function(item) {
return !arrN.includes(item))
}
Full snippet:
function callM(arrays) {
const arr0 = arrays.shift()
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
//const unique = arrN.filter(function(elem, index, self) {
// return index == self.indexOf(elem)
//})
return arr0.filter(function(item) {
return !arrN.includes(item)
})
}
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10]]))
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]]))
of course ES6 would be easier. ;)
const callM = (first, ...rest) => {
const arrays = [].concat(...rest)
return first.filter(item => !arrays.includes(item))
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
A short solution for small and medium sized arrays:
// Return elements in array but not in filters:
function difference(array, ...filters) {
return array.filter(el => !filters.some(filter => filter.includes(el)));
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 1, 10], [7, 2, 8])); // [3, 4]
For large inputs, consider creating a Set from all filters and filtering in linear time using set.has(el).
In order to fix your implementation, you could label the outer for-loop and continue from there whenever a filter contains one of the array elements. Only when all filters pass without match, you push the array element into the result:
// Return elements in array but not in filters:
function difference(array, ...filters) {
const result = [];
loop: for (const el of array) {
for (const filter of filters) {
if (filter.includes(el)) continue loop;
}
result.push(el);
}
return result;
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3,4]
If you're willing to use Underscore, you can do this in one line of code:
console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
https://jsfiddle.net/o1zuaa6m/
You can use array#reduce to create object lookup of all the other array excluding the first array. Then use array#filter to get the values which are not present in the object lookup
var callM = (first, ...rest) => {
var combined = rest
.reduce((res,arr) => res.concat(arr))
.reduce((o, v) => {
o[v] = true;
return o;
},{});
return first
.filter(v => !combined[v]);
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The "proper" way to exclude values is usually to use a lookup hash set with the values to exclude:
const callM = (a, ...b) => (b = new Set(b.concat.apply(...b)), a.filter(v => !b.has(v)))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3, 4]

Filter intersection of arrays (Javascript)

I'm trying to write a function that finds arrays that are not common to both nested arrays. Also note the arrays will be pre-sorted.
var array1 = [ [1, 2, 3], [2, 3, 4] [5, 6, 7] ];
var array2 = [ [1, 2, 3], [2, 3, 4] [7, 8, 9] ];
For the above two arrays the function should return [5, 6, 7] and [7, 8, 9].
So far I've got:
function arrayIntersection(array1, array2) {
return array2.filter(function(values1) {
return array2.indexOf(values1) === -1;
});
};
But it doesn't seem to be working. I'm guessing the indexOf() isn't doing a compares correctly. I'm trying to avoid using ES6 or polyfills.
You can use the built in .every() and .filter() array methods to accomplish this task.
var array1 = [ [1, 2, 3], [2, 3, 4], [5, 6, 7] ];
var array2 = [ [1, 2, 3], [2, 3, 4], [7, 8, 9] ];
function compareArrays(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
} else {
return arr1.every(function(elem) {
return arr2.indexOf(elem) > -1;
});
}
}
function filterUnique(arr1, arr2) {
return arr1.filter(function(elem) {
return arr2.every(function(_elem) {
return !compareArrays(_elem, elem);
});
});
}
function filterIntersection(arr1, arr2) {
var uniqueToArr1 = filterUnique(arr1, arr2);
var uniqueToArr2 = filterUnique(arr2, arr1);
return [].concat(uniqueToArr1, uniqueToArr2);
}
console.log(filterIntersection(array1, array2));
First, you referenced the wrong array in your filter. To fix the comparison, you could turn the arrays to json. You will also need to run the filter against the second array and join the answers.:
var array1 = [ [1, 2, 3], [2, 3, 4], [5, 6, 7] ];
var array2 = [ [1, 2, 3], [2, 3, 4], [7, 8, 9] ];
function arrayIntersection(input1, input2) {
var input2NotInInput1 = input2.filter(function(values1) {
return input1.map(function(val) { return JSON.stringify(val); }).indexOf(JSON.stringify(values1)) === -1;
});
var input1NotInInput2 = input1.filter(function(values1) {
return input2.map(function(val) { return JSON.stringify(val); }).indexOf(JSON.stringify(values1)) === -1;
});
return input1NotInInput2 .concat( input2NotInInput1 );
};
console.log(arrayIntersection(array1, array2));
It's not a best option but it works
var ar1 = [
[1, 2, 3],
[2, 3, 4],
[5, 6, 7]
];
var ar2 = [
[1, 2, 3],
[2, 3, 4],
[7, 8, 9]
];
function arrayIntersection(array1, array2) {
return array2.filter(function(values1) {
return !array1.some(v => JSON.stringify(v) === JSON.stringify(values1));
});
};
var result = arrayIntersection(ar1, ar2);
console.log(result);

underscore or lazy.js map (0,1,2,3,4) + (1,2,3,4,5) ->(1,3,5,7,9)

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

Categories