This question already has answers here:
lodash: Get duplicate values from an array
(13 answers)
Closed 6 years ago.
I'm using lodash and I have an array:
const arr = ['firstname', 'lastname', 'initials', 'initials'];
I want a new array containing only the values that appear more than once (the duplicate values).
It seems like this is something lodash might have a specific method for, but I can't see one. Something like: const dups = _.duplicates(arr); would be nice.
I've got:
// object with array values and number of occurrences
const counts = _.countBy(arr, value => value);
// reduce object to only those with more than 1 occurrence
const dups = _.pickBy(counts, value => (value > 1));
// just the keys
const keys = _.keys(dups);
console.log(keys); // ['initials']
Is there a better way than this..?
It's not necessary to use lodash for this task, you can easily achieve it using plain JavaScript with Array.prototype.reduce() and Array.prototype.indexOf():
var arr = ['firstname', 'lastname', 'initials', 'initials', 'a', 'c', 'a', 'a', 'c'];
var dupl = arr.reduce(function(list, item, index, array) {
if (array.indexOf(item, index + 1) !== -1 && list.indexOf(item) === -1) {
list.push(item);
}
return list;
}, []);
console.log(dupl); // prints ["initials", "a", "c"]
Check the working demo.
Or a bit simpler with lodash:
var arr = ['firstname', 'lastname', 'initials', 'initials', 'a', 'c', 'a', 'a', 'c'];
var dupl = _.uniq(_.reject(arr, function(item, index, array) {
return _.indexOf(array, item, index + 1) === -1;
}));
console.log(dupl); // prints ["initials", "a", "c"]
You can use this
let dups = _.filter(array, (val, i, it) => _.includes(it, val, i + 1));
If you only want unique duplicates in your dups array, you may use _.uniq() on it.
Related
I'm creating a function in angular and I want to reduce a list of objects like this example :
var tab =[{name:'i', value:13}, {name:'j', value:10},{name:'i', value:8}]
I want to get this result :
tab = [{name:'i', value:21},{name:'j', value:10}]
You can check whether the array contains an object with the same name property with Array.find, and if so, increment the value property. Otherwise, push to the array.
var tab = [{name: 'i', value: 13}, {name: 'j', value: 10}, {name: 'i', value: 8}]
const result = tab.reduce((a, b) => {
let found = a.find(e => e.name == b.name);
return found ? found.value += b.value : a.push(b), a
}, [])
console.log(result);
The answer provided by Spectric is a default solution for this problem and would be preferred in most cases as it is clear and straightforward. However I asked myself if we can solve it without additional iteration (via find function) and came up with following solution.
var tab = [{ name: 'i', value: 13 }, { name: 'j', value: 10 }, { name: 'i', value: 8 }, { name: 'j', value: 8 }];
const result = tab.reduce((a, b, i) => {
let found = a.tmp.hasOwnProperty(b.name);
return !found ? a.res[a.tmp[b.name] = i] = b : a.res[a.tmp[b.name]].value += b.value, a;
}, { tmp: {}, res: [] }).res;
console.log(result);
It is adding auxiliary tmp object saving index of a given unique element in the result array and allows us to do everything in one iteration. I could imagine this to be better in terms of performance if we had a lot of objects of only few distinct types.
If you are just chopping off the last element within an array you should be able to just use the reduceRight() function. Click here for more on the reduce Method
let tab =[{name:'i', value:13}, {name:'j', value:10},{name:'k', value:8}]
tab.reduceRight();
console.log(tab);
I have the following array which has strings in it
const dict = ['original sound', 'الصوت الأصلي', 'оригинальный звук'];
Now I want to filter it based on the array using object filter
Object.filter = (obj, predicate) =>
Object.keys(obj)
.filter( key => predicate(obj[key]) )
.reduce( (res, key) => (res[key] = obj[key], res), {} );
let filtered = Object.filter(audio, audio =>
audio.audio.title !== dict
);
To be clear I don't want any music with the titles that match the dict array
Check if the array doesn't include that element:
let filtered = Object.filter(audio, audio =>
!dict.includes(audio.audio.title)
);
If I understand correctly, you want to filter an array of strings, based on another string array as dictionary, if so, try below
var filtered = ['a', 'b', 'c', 'd'].filter(
function(e) {
return this.indexOf(e) < 0;
},
['b', 'd']
);
becarefull of string.includes(anotherString) is NOT working on IE
I have two arrays:
var a = ['a', 'as', 'sa'];
var b = ['sa', 'a', 'as'];
Is there anything special in shouldJS to test if these two arrays have same items? Anything Like
should(a).be.xyz(b)
that can test them? Here, xyz is what I am looking for.
A naive, but possibly sufficient solution would be to sort the arrays before comparing them:
should(a.sort()).be.eql(b.sort())
Note that sort() works in-place, mutating the original arrays.
You could implement this with should's Assertion.add feature. For example:
var a = ['a', 'as', 'sa'];
var b = ['sa', 'a', 'as'];
should.Assertion.add('haveSameItems', function(other) {
this.params = { operator: 'to be have same items' };
this.obj.forEach(item => {
//both arrays should at least contain the same items
other.should.containEql(item);
});
// both arrays need to have the same number of items
this.obj.length.should.be.equal(other.length);
});
//passes
a.should.haveSameItems(b);
b.push('d');
// now it fails
a.should.haveSameItems(b);
Slightly improved version of Michael's code:
should.Assertion.add("haveSameItems", function (other) {
this.params = { operator: "to be have same items" };
const a = this.obj.slice(0);
const b = other.slice(0);
function deepSort(objA, objB) {
const a = JSON.stringify(objA);
const b = JSON.stringify(objB);
return (a < b ? -1 : (a > b ? 1 : 0));
}
a.sort(deepSort);
b.sort(deepSort);
a.should.be.deepEqual(b);
});
So I know how to set the key dynamically like this:
var hashObj = {};
hashObj[someValue] = otherValue;
But I haven't seen any answer regarding map():
var list = ['a', 'b', 'c'];
var hashObject = list.map(function(someValue) {
return { someValue: 'blah' };
});
// should return: [ {'a': 'blah'}, {'b': 'blah'}, {'c': 'blah'} ];
I know I can do this in a for loop and such, but is this not possible in javascript using just map()?
You need to get someValue to be evaluated as its value. If you use object notation, it will be interpreted literally as string.
You can use a temporary object to achieve what you want:
var list = ['a', 'b', 'c'];
var hashObject = list.map(function(someValue) {
var tmp = {};
tmp[someValue] = 'blah';
return tmp;
});
I know it is really old question, but answer can be helpful for others.
As it has been already said, Array.prototype.map is used to get new array.
But if you want to get object instead of array - maybe you should think about using Array.prototype.reduce (https://developer.mozilla.org/pl/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)?
const list = ['a', 'b', 'c'];
const hashObject = list.reduce((acc, current) => {
acc[current] = 'blah';
return acc;
}, {});
// hashObject equals: {"a":"blah","b":"blah","c":"blah"}
And if you want achieve the same result as mentioned in your question, you can use Array.prototype.map of course:
const list = ['a', 'b', 'c'];
const hashArrayOfObjects = list.map((current) => {
return {[current]: 'blah'};
});
// hashArrayOfObjects equals: [{"a":"blah"},{"b":"blah"},{"c":"blah"}]
You can check how it works on CodePen: https://codepen.io/grygork/pen/PojNrXO
Commonly the 'map()' method is used to get new array from each return value.
In your case, I recommend to use forEach().
var list = ['a','b','c'];
var hashObject = {};
list.forEach( function( key ) {
hashObject[ key ] = 'blah';
});
Or use object() of underscore.js library
var list = ['a','b','c'];
var hashObject = _.object( list ); // { a: undefined, b: undefined, c: undefined }
hashObject = _.mapObject( hashObject, function( val, key ) {
return 'blah';
});
Then again, Array.prototype.map is only used to get new 'array' not 'object'.
I have:
var array = new Array();
array.push("A");
array.push("B");
array.push("C");
I want to be able to do something like:
array.remove("B");
but there is no remove function. How do I accomplish this?
I'm actually updating this thread with a more recent 1-line solution:
let arr = ['A', 'B', 'C'];
arr = arr.filter(e => e !== 'B'); // will return ['A', 'C']
The idea is basically to filter the array by selecting all elements different to the element you want to remove.
Note: will remove all occurrences.
EDIT:
If you want to remove only the first occurence:
t = ['A', 'B', 'C', 'B'];
t.splice(t.indexOf('B'), 1); // will return ['B'] and t is now equal to ['A', 'C', 'B']
Loop through the list in reverse order, and use the .splice method.
var array = ['A', 'B', 'C']; // Test
var search_term = 'B';
for (var i=array.length-1; i>=0; i--) {
if (array[i] === search_term) {
array.splice(i, 1);
// break; //<-- Uncomment if only the first term has to be removed
}
}
The reverse order is important when all occurrences of the search term has to be removed. Otherwise, the counter will increase, and you will skip elements.
When only the first occurrence has to be removed, the following will also work:
var index = array.indexOf(search_term); // <-- Not supported in <IE9
if (index !== -1) {
array.splice(index, 1);
}
List of One Liners
Let's solve this problem for this array:
var array = ['A', 'B', 'C'];
1. Remove only the first:
Use If you are sure that the item exist
array.splice(array.indexOf('B'), 1);
2. Remove only the last:
Use If you are sure that the item exist
array.splice(array.lastIndexOf('B'), 1);
3. Remove all occurrences:
array = array.filter(v => v !== 'B');
DEMO
You need to find the location of what you're looking for with .indexOf() then remove it with .splice()
function remove(arr, what) {
var found = arr.indexOf(what);
while (found !== -1) {
arr.splice(found, 1);
found = arr.indexOf(what);
}
}
var array = new Array();
array.push("A");
array.push("B");
array.push("C");
remove(array, 'B');
alert(array);
This will take care of all occurrences.
Simply
array.splice(array.indexOf(item), 1);
Simple solution (ES6)
If you don't have duplicate element
Array.prototype.remove = function(elem) {
var indexElement = this.findIndex(el => el === elem);
if (indexElement != -1)
this.splice(indexElement, 1);
return this;
};
Online demo (fiddle)
const changedArray = array.filter( function(value) {
return value !== 'B'
});
or you can use :
const changedArray = array.filter( (value) => value === 'B');
The changedArray will contain the without value 'B'
In case of wanting to remove array of strings from array of strings:
const names = ['1','2','3','4']
const excludeNames = ['2','3']
const filteredNames = names.filter((name) => !excludeNames.includes(name));
// ['1','4']
You have to write you own remove. You can loop over the array, grab the index of the item you want to remove, and use splice to remove it.
Alternatively, you can create a new array, loop over the current array, and if the current object doesn't match what you want to remove, put it in a new array.
use:
array.splice(2, 1);
This removes one item from the array, starting at index 2 (3rd item)
use array.splice
/*array.splice(index , howMany[, element1[, ...[, elementN]]])
array.splice(index) // SpiderMonkey/Firefox extension*/
array.splice(1,1)
Source:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
This only valid on str list, look up this
myStrList.filter(item=> !["deletedValue","deletedValue2"].includes(item))
Here is the simplest answer.
First find index using indexofand then
if index exist use splice
const array = ['apple', 'banana', 'orange', 'pear'];
const index = array.indexOf('orange'); // Find the index of the element to remove
if (index !== -1) { // Make sure the element exists in the array
array.splice(index, 1); // Remove the element at the found index
}
console.log(array); // ["apple", "banana", "pear"]