I am able to find the object in the javascript below but it is pretty horrible and I am doing the find twice.
I'd be interested in a better way and one that did not involve lodash which I am current not using.
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const selected = statuses.find(status => {
const none = status.items.find(alert => {
return alert.name === 'none';
});
return !!none;
});
console.log(selected)
const a = selected.items.find(s => s.name === 'none');
console.log(a)
You could use a nested some and find like this. This way you can skip the the operation once a match is found.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
let found;
statuses.some(a => {
const s = a.items.find(i => i.name === "none");
if (s) {
found = s;
return true
} else {
return false
}
})
console.log(found)
You could do something like this using map, concat and find. This is a bit slower but looks neater.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
const found = [].concat(...statuses.map(a => a.items))
.find(a => a.name === "none")
console.log(found)
Here's a jsperf comparing it with your code. The first one is the fastest.
You could combine all the status items into one array with reduce() and then you only have to find() once:
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const theNones = statuses
.reduce(function(s, t) { return s.items.concat(t.items); })
.find(function(i) { return i.name === 'none'; });
console.log(theNones);
You could use flatMap and find:
Note: Array.prototype.flatMap is in Stage 3 and not part of the language yet but ships in most environments today (including Babel).
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr
.flatMap(({ items }) => items)
.find(({
name
}) => name === 'none')
)
We could polyfill the flatting with concating (via map + reduce):
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
console.log(
arr
.map(({ items }) => items)
.reduce((items1, items2) => items1.concat(items2))
.find(({
name
}) => name === 'none')
)
You could also reduce the result:
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr.reduce((result, {
items
}) =>
result ? result : items.find(({
name
}) => name === 'none'), null)
)
One reduce function (one iteration over everything), that returns an array of any object matching the criteria:
const [firstOne, ...others] = statuses.reduce((found, group) => found.concat(group.items.filter(item => item.name === 'none')), [])
I used destructuring to mimic your find idea, as you seem chiefly interested in the first one you come across. Because this iterates only once over each item, it is better than the alternative answers in terms of performance, but if you are really concerned for performance, then a for loop is your best bet, as the return will short-circuit the function and give you your value:
const findFirstMatchByName = (name) => {
for (let group of statuses) {
for (let item of group.items) {
if (item.name === name) {
return item
}
}
}
}
findFirstMatchByName('none')
Related
I have an array of contact cards that hold names & addresses etc for users. What I want to do is create another array that removes any duplicate addresses (ie people in the same household) and creates a name that is a combination of the two. For example:
{flatNum: 1, Name: "ken"},{flatNum: 1, Name: "bob"}, {flatNum: 2, Name: "emma"}
would become:
{flatNum: 1, Name: "ken & bob"}, {flatNum: 2, Name: "emma"}
I know how I can achieve this with a long for loop type thing but was hoping to find a more concise method. I am assuming that reduce would be the key and have been playing around. Currently got this:
let contactCardsComb = contactCards.reduce(function(a,b){
if (a.flatNum == b.flatNum){
return a.Name = a.Name+b.Name;
}
});
Which is obviously horribly wrong but any pointers would be great
You could group by flatNum and get the values from the object.
const
data = [{ flatNum: 1, Name: "ken" }, { flatNum: 1, Name: "bob" }, { flatNum: 2, Name: "emma" }],
result = Object.values(data.reduce((r, { flatNum, Name }) => {
if (r[flatNum]) r[flatNum].Name += ' & ' + Name;
else r[flatNum] = { flatNum, Name };
return r;
}, {}));
console.log(result);
I take the OP's Q. as a chance for proofing that an abstract but (highly) configurable reduce task is in no time (less than a minute, here for the OP's problem) suitable for a variety of problems that at first sight do have nothing in common, due to the different environments and the naming of variables etc.
If one takes e.g. the following approach of this answer to another Q. ... "Segregate an array based on same name") ... some days ago, one ...
just needs to change the implementation (line 24) of how to merge the properties of two to be grouped items, and
just has to provide the correct key name (line 42) of the target value one wants to group around all the other list items.
... code ...
// reduce function that groups
// any data item generically by key.
function groupByKeyAndMergeProperties(collector, item) {
const { merge, key, index, list } = collector;
const groupKey = item[key];
let groupItem = index[groupKey];
if (!groupItem) {
// use `Object.assign` initially in order
// to not mutate the original (list's) reference.
groupItem = index[groupKey] = Object.assign({}, item);
list.push(groupItem);
merge(groupItem, null);
} else {
merge(groupItem, item);
}
return collector;
}
// task specific merge function,
// here, according to the OP's goal.
function mergeItemNames(targetItem, sourceItem) {
if (sourceItem !== null) {
targetItem.Name = `${ targetItem.Name } & ${ sourceItem.Name }`;
}
}
const sampleList = [
{ flatNum: 1, Name: "ken" },
{ flatNum: 1, Name: "bob" },
{ flatNum: 2, Name: "emma" },
{ flatNum: 2, Name: "april" },
{ flatNum: 2, Name: "june" },
{ flatNum: 3, Name: "john" }
];
console.log(
sampleList.reduce(groupByKeyAndMergeProperties, {
// task specific reduce configuration.
merge: mergeItemNames,
key: 'flatNum',
index: {},
list: []
}).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
this can be easily accomplished by using lodash library,
let contactCards = [
{flatNum: 1, Name: "ken"},
{flatNum: 1, Name: "bob"},
{flatNum: 2, Name: "emma"},
{flatNum: 2, Name: "april"},
{flatNum: 2, Name: "june"}
];
let tempGroup = _.groupBy(contactCards, contact => {
return contact.flatNum;
});
console.log('tempGroup :', tempGroup);
let contactCardsComb = Object
.values(tempGroup)
.map( e => e.reduce( (a,b) =>
({...a, Name: `${ a.Name } & ${ b.Name }` })
));
console.log('result : ', contactCardsComb);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.min.js"></script>
// a vanilla approach would be as follows
let flatNums = Array.from(new Set(contactCards.map( e => e.flatNum)))
let groupedContactCards = flatNums
.map( e => contactCards
.filter(contact => contact.flatNum === e))
let reducedContactCards = groupedContactCards
.map( e => e
.reduce((a,b) => ({...a,Name:`${a.Name} & ${b.Name}`})))
console.log(reducedContactCards)
or we can wrap this as below
let reducedContactCards = Array.from(new Set(contactCards.map( e => e.flatNum)))
.map( e => contactCards
.filter(contact => contact.flatNum === e))
.map( e => e.reduce((a,b) => ({...a,Name:`${a.Name} & ${b.Name}`})))
console.log(reducedContactCards)
I have read a few questions and answers on it already. It looks like my recursive function has got enough "return" statements, so... I do not know why it returns undefined... I have added extra log statement to show that the function itself finds the element, but does not return it...
let animals = [
{
name: "dogs",
id: 1,
children: [
{
name: "lessie",
id: 2
},
{
name: "bark-a-lot",
id: 3
}
]
},
{
name: "cats",
id: 4,
children: [
{
name: "meows-a-lot",
id: 5,
children: [
{
name: "meows-a-lot-in-the-morning",
id: 6
}
]
},
{
name: "whisk-ass",
id: 7
}
]
}
];
function recurseFind(node, id) {
if (Array.isArray(node)) {
return node.forEach(el => {
return recurseFind(el, id);
});
} else {
if (node.id === id) {
console.log("node matched", node.id, id, node);
return node;
} else if (node.children) {
return node.children.forEach(child => {
return recurseFind(child, id);
});
} else {
return "not found";
}
}
}
const found = recurseFind(animals, 6);
console.log("found", found, "wtf");
forEach returns undefined, so
return node.forEach(el => {
return recurseFind(el, id);
});
will always return undefined, no matter what the recursive calls find.
I'd use a for loop instead, and if a match is found, return it:
let animals = [
{
name: "dogs",
id: 1,
children: [
{
name: "lessie",
id: 2
},
{
name: "bark-a-lot",
id: 3
}
]
},
{
name: "cats",
id: 4,
children: [
{
name: "meows-a-lot",
id: 5,
children: [
{
name: "meows-a-lot-in-the-morning",
id: 6
}
]
},
{
name: "whisk-ass",
id: 7
}
]
}
];
function recurseFind(node, id) {
if (Array.isArray(node)) {
for (const el of node) {
const result = recurseFind(el, id);
if (result) return result;
}
} else {
if (node.id === id) {
return node;
} else if (node.children) {
for (const child of node.children) {
const result = recurseFind(child, id);
if (result) return result;
}
}
}
}
const found = recurseFind(animals, 6) || 'not found';
console.log("found", found);
VLAZ and CertainPerformance already pointed out why your function wasn't working.
Here's an alternative technique that seems a little simpler to me:
const recursiveFind = (pred) => (xs) => xs .reduce (
(r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred) (x.children || []) || null,
null
)
const findById = (id) => recursiveFind(x => x.id == id)
const animals = [{name: "dogs", id: 1, children: [{name: "lessie", id: 2}, {name: "bark-a-lot", id: 3}]}, {name: "cats", id: 4, children: [{name: "meows-a-lot", id: 5, children: [{ name: "meows-a-lot-in-the-morning", id: 6}]}, {name: "whisk-ass", id: 7}]}];
console .log (findById (3) (animals))
console .log (findById (4) (animals))
We start with a generic function that searches for objects nested this way by whether they match the supplied predicate function. Then we pass it the predicate x => x.id == id to create a function that takes an id and then a list of values and finds the first value with matching ids in the list, or null if none are found.
If you have absolutely no use for this recursiveFind function, you can inline it into findById like this:
const findById = (id, xs) => xs .reduce (
(r, x) => r != null ? r : x.id == id ? x : findById (id, x.children || []) || null,
null
)
findById (3, animals)
But I actually would prefer to go in the other direction, and make it still more generic, using something like this:
const recursiveFind = (pred, descend) => (xs) => xs .reduce (
(r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred, descend) (descend (x) || []) || null,
null
)
const findById = (id) => recursiveFind (x => x.id == id, x => x.children)
findById (3) (animals)
This version also parameterizes how we descend into the children of a node. In this case, we simply use x => x.children, but it's easy to imagine using other properties or a more complex method.
In all of these, do note that the function processes all nodes of your nested array structure, even when we've already found a match. If we have, the first check (r != null) skips ahead quickly, but if performance is critical, you might prefer a solution with explicit short-circuiting loops such as the one from CertainPerformance.
I have the following Array and I'm trying to print the child array value using .find by providing an ID
connections = [
{
group: 'a',
items: [
{
id: '1',
name: 'Andre'
},
{
id: '2',
name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3',
name: 'Brandon'
}
]
},
]
I have tried the following in my Angular app,
getUser(id) {
this.activeItem = this.connections.items.find(data => data.id === id);
console.log(this.activeItem);
}
I'm providing the correct ID but I'm getting an error saying,
error TS2339: Property 'items' does not exist on type....
Thank you.
You can use filter and some methods. This approach will filter array and your array will contain only desired items:
let connections = [
{
group: 'a',
items: [
{
id: '1', name: 'Andre'
},
{
id: '2', name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3', name: 'Brandon'
}
]
},
]
let id = 3;
// ONE WAY
const result = connections.filter(f=> f.items.some(s=> s.id == id))
.flatMap(fm => fm.items);
console.log(`result: `, result);
// OR ANOTHER WAY:
const resultWithGroup = connections.filter(f=> f.items.some(s=> s.id == id));
const resultItem = Object.assign({}, ...resultWithGroup).items.find(f => f.id == id);
console.log(`resultItem: `, resultItem);
console.log(`resultItem as an array: `, [resultItem]);
In addition, it is possible to use flatMap method. By using this approach you are getting all items with desired id and then find the first element with id == 3:
let connections = [
{
group: 'a',
items: [
{
id: '1', name: 'Andre'
},
{
id: '2', name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3', name: 'Brandon'
}
]
},
]
const result = connections.flatMap(f => f.items).find(f => f.id == id);
console.log(`result as array`, [result]);
You can use flatMap
Try like this:
getUser(id) {
this.activeItem = this.connections.flatMap(x => x.items).find(data => data.id === id);
console.log(this.activeItem);
}
Working Demo
As i can see from the Json object, the items arrays are grouped inside their parent objects. So first you would have to flatten the grouped array:
let items = []
connections.forEach(obj => obj.items.forEach( item => items.push(item)))
Now the items array would only be item objects so it will be easier to do a find:
items.find(item => item.id == 3)
You are trying to use an array without specifying the element of the array
-----------------\/ here
this.connections[0].items.find(data => data.id === id);
The reason your "this.connections.items.find" is not working is that connections variable here represents an array of objects, you cannot directly access a key that is inside an objects contained in an array of objects.
Use this code instead:
this.activeItem = this.connections.filter(obj => obj.items.find(val => val.id == id));
console.log(this.activeItem.items);
connections variable is an array and you are trying to access it as an object. PFB the below code it should be working fine for you.
getUser(id) {
this.activeItem = connections.find(function(element) { return element.items.find(function(el){return el.id==id;}); });
console.log(this.activeItem);
}
Try this. (You made mistake with connections.items)
getUser(id) {
let activeItemIndex = -1;
this.connections.forEach((c) => {
activeItemIndex = c.items.findIndex(item => item.id === id);
if (activeItemIndex > -1) {
this.activeItem = c.items[activeItemIndex];
}
});
}
It is normal that you get this error: "error TS2339: Property 'items' does not exist on type....".
Actually, 'connections' is an array and does not have any prperty 'items'.
'items' is an attribut of the elements contained in 'connections' array.
You can try something like:
getUser(id) {
for (const element of this.connections) {
this.activeItem = element.items.find(data => data.id === id);
if (this.activeItem) {
break;
}
}
console.log(this.activeItem);
}
Once 'this.activeItem' is found we exit the loop with the 'break;' statement.
You have to specify index of connections.
But this is the better solution, Because you have all the users in one place:
getUser(id) {
users = connections.reduce((users, item)=>{
users.push(...item.items);
return users;
}, []);
this.activeItem = users.find(data => data.id === id);
console.log(this.activeItem);
}
I need some help with iterating through array, I keep getting stuck or reinventing the wheel.
values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
How could I check if there are two (or more) same name value in array? I do not need a counter, just setting some variable if array values are not unique. Have in mind that array length is dynamic, also array values.
Use array.prototype.map and array.prototype.some:
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){
return valueArr.indexOf(item) != idx
});
console.log(isDuplicate);
ECMA Script 6 Version
If you are in an environment which supports ECMA Script 6's Set, then you can use Array.prototype.some and a Set object, like this
let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
return seen.size === seen.add(currentObject.name).size;
});
Here, we insert each and every object's name into the Set and we check if the size before and after adding are the same. This works because Set.size returns a number based on unique data (set only adds entries if the data is unique). If/when you have duplicate names, the size won't increase (because the data won't be unique) which means that we would have already seen the current name and it will return true.
ECMA Script 5 Version
If you don't have Set support, then you can use a normal JavaScript object itself, like this
var seen = {};
var hasDuplicates = values.some(function(currentObject) {
if (seen.hasOwnProperty(currentObject.name)) {
// Current name is already seen
return true;
}
// Current name is being seen for the first time
return (seen[currentObject.name] = false);
});
The same can be written succinctly, like this
var seen = {};
var hasDuplicates = values.some(function (currentObject) {
return seen.hasOwnProperty(currentObject.name)
|| (seen[currentObject.name] = false);
});
Note: In both the cases, we use Array.prototype.some because it will short-circuit. The moment it gets a truthy value from the function, it will return true immediately, it will not process rest of the elements.
In TS and ES6 you can create a new Set with the property to be unique and compare it's size to the original array.
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName3' },
{ name: 'someName1' }
]
const uniqueValues = new Set(values.map(v => v.name));
if (uniqueValues.size < values.length) {
console.log('duplicates found')
}
To know if simple array has duplicates we can compare first and last indexes of the same value:
The function:
var hasDupsSimple = function(array) {
return array.some(function(value) { // .some will break as soon as duplicate found (no need to itterate over all array)
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
})
}
Tests:
hasDupsSimple([1,2,3,4,2,7])
// => true
hasDupsSimple([1,2,3,4,8,7])
// => false
hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true
For an array of objects we need to convert the objects values to a simple array first:
Converting array of objects to the simple array with map:
var hasDupsObjects = function(array) {
return array.map(function(value) {
return value.suit + value.rank
}).some(function(value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value);
})
}
Tests:
var cardHand = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"three" },
]
hasDupsObjects(cardHand);
// => false
var cardHand2 = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"ten" },
]
hasDupsObjects(cardHand2);
// => true
if you are looking for a boolean, the quickest way would be
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
console.log(foundDuplicateName)
Found the first one duplicate name
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
You just need one line of code.
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;
Try an simple loop:
var repeat = [], tmp, i = 0;
while(i < values.length){
repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}
Demo
With Underscore.js A few ways with Underscore can be done. Here is one of them. Checking if the array is already unique.
function isNameUnique(values){
return _.uniq(values, function(v){ return v.name }).length == values.length
}
With vanilla JavaScript
By checking if there is no recurring names in the array.
function isNameUnique(values){
var names = values.map(function(v){ return v.name });
return !names.some(function(v){
return names.filter(function(w){ return w==v }).length>1
});
}
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;
}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);
You can use map to return just the name, and then use this forEach trick to check if it exists at least twice:
var areAnyDuplicates = false;
values.map(function(obj) {
return obj.name;
}).forEach(function (element, index, arr) {
if (arr.indexOf(element) !== index) {
areAnyDuplicates = true;
}
});
Fiddle
Adding updated es6 function to check for unique and duplicate values in array. This function is modular and can be reused throughout the code base. Thanks to all the post above.
/* checks for unique keynames in array */
const checkForUnique = (arrToCheck, keyName) => {
/* make set to remove duplicates and compare to */
const uniqueValues = [...new Set(arrToCheck.map(v => v[keyName]))];
if(arrToCheck.length !== uniqueValues.length){
console.log('NOT UNIQUE')
return false
}
return true
}
let arr = [{name:'joshua'},{name:'tony'},{name:'joshua'}]
/* call function with arr and key to check for */
let isUnique = checkForUnique(arr,'name')
checkDuplicate(arr, item) {
const uniqueValues = new Set(arr.map((v) => v[item]));
return uniqueValues.size < arr.length;
},
console.log(this.checkDuplicate(this.dutyExemptionBase, 'CI_ExemptionType')); // true || false
It is quite interesting to work with arrays
You can use new Set() method to find duplicate values!
let's assume you have an array of objects like this...
let myArray = [
{ id: 0, name: "Jhon" },
{ id: 1, name: "sara" },
{ id: 2, name: "pop" },
{ id: 3, name: "sara" }
]
const findUnique = new Set(myArray.map(x => {
return x.name
}))
if(findUnique.size < myArray.length){
console.log("duplicates found!")
}else{
console.log("Done!")
}
const duplicateValues = [{ name: "abc" }, { name: "bcv" }, { name: "abc" }];
const isContainDuplicate = (params) => {
const removedDuplicate = new Set(params.map((el) => el.name));
return params.length !== removedDuplicate.size;
};
const isDuplicate = isContainDuplicate(duplicateValues);
console.log("isDuplicate");
I need some help with iterating through array, I keep getting stuck or reinventing the wheel.
values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
How could I check if there are two (or more) same name value in array? I do not need a counter, just setting some variable if array values are not unique. Have in mind that array length is dynamic, also array values.
Use array.prototype.map and array.prototype.some:
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){
return valueArr.indexOf(item) != idx
});
console.log(isDuplicate);
ECMA Script 6 Version
If you are in an environment which supports ECMA Script 6's Set, then you can use Array.prototype.some and a Set object, like this
let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
return seen.size === seen.add(currentObject.name).size;
});
Here, we insert each and every object's name into the Set and we check if the size before and after adding are the same. This works because Set.size returns a number based on unique data (set only adds entries if the data is unique). If/when you have duplicate names, the size won't increase (because the data won't be unique) which means that we would have already seen the current name and it will return true.
ECMA Script 5 Version
If you don't have Set support, then you can use a normal JavaScript object itself, like this
var seen = {};
var hasDuplicates = values.some(function(currentObject) {
if (seen.hasOwnProperty(currentObject.name)) {
// Current name is already seen
return true;
}
// Current name is being seen for the first time
return (seen[currentObject.name] = false);
});
The same can be written succinctly, like this
var seen = {};
var hasDuplicates = values.some(function (currentObject) {
return seen.hasOwnProperty(currentObject.name)
|| (seen[currentObject.name] = false);
});
Note: In both the cases, we use Array.prototype.some because it will short-circuit. The moment it gets a truthy value from the function, it will return true immediately, it will not process rest of the elements.
In TS and ES6 you can create a new Set with the property to be unique and compare it's size to the original array.
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName3' },
{ name: 'someName1' }
]
const uniqueValues = new Set(values.map(v => v.name));
if (uniqueValues.size < values.length) {
console.log('duplicates found')
}
To know if simple array has duplicates we can compare first and last indexes of the same value:
The function:
var hasDupsSimple = function(array) {
return array.some(function(value) { // .some will break as soon as duplicate found (no need to itterate over all array)
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
})
}
Tests:
hasDupsSimple([1,2,3,4,2,7])
// => true
hasDupsSimple([1,2,3,4,8,7])
// => false
hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true
For an array of objects we need to convert the objects values to a simple array first:
Converting array of objects to the simple array with map:
var hasDupsObjects = function(array) {
return array.map(function(value) {
return value.suit + value.rank
}).some(function(value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value);
})
}
Tests:
var cardHand = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"three" },
]
hasDupsObjects(cardHand);
// => false
var cardHand2 = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"ten" },
]
hasDupsObjects(cardHand2);
// => true
if you are looking for a boolean, the quickest way would be
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
console.log(foundDuplicateName)
Found the first one duplicate name
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
You just need one line of code.
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;
Try an simple loop:
var repeat = [], tmp, i = 0;
while(i < values.length){
repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}
Demo
With Underscore.js A few ways with Underscore can be done. Here is one of them. Checking if the array is already unique.
function isNameUnique(values){
return _.uniq(values, function(v){ return v.name }).length == values.length
}
With vanilla JavaScript
By checking if there is no recurring names in the array.
function isNameUnique(values){
var names = values.map(function(v){ return v.name });
return !names.some(function(v){
return names.filter(function(w){ return w==v }).length>1
});
}
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;
}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);
You can use map to return just the name, and then use this forEach trick to check if it exists at least twice:
var areAnyDuplicates = false;
values.map(function(obj) {
return obj.name;
}).forEach(function (element, index, arr) {
if (arr.indexOf(element) !== index) {
areAnyDuplicates = true;
}
});
Fiddle
Adding updated es6 function to check for unique and duplicate values in array. This function is modular and can be reused throughout the code base. Thanks to all the post above.
/* checks for unique keynames in array */
const checkForUnique = (arrToCheck, keyName) => {
/* make set to remove duplicates and compare to */
const uniqueValues = [...new Set(arrToCheck.map(v => v[keyName]))];
if(arrToCheck.length !== uniqueValues.length){
console.log('NOT UNIQUE')
return false
}
return true
}
let arr = [{name:'joshua'},{name:'tony'},{name:'joshua'}]
/* call function with arr and key to check for */
let isUnique = checkForUnique(arr,'name')
checkDuplicate(arr, item) {
const uniqueValues = new Set(arr.map((v) => v[item]));
return uniqueValues.size < arr.length;
},
console.log(this.checkDuplicate(this.dutyExemptionBase, 'CI_ExemptionType')); // true || false
It is quite interesting to work with arrays
You can use new Set() method to find duplicate values!
let's assume you have an array of objects like this...
let myArray = [
{ id: 0, name: "Jhon" },
{ id: 1, name: "sara" },
{ id: 2, name: "pop" },
{ id: 3, name: "sara" }
]
const findUnique = new Set(myArray.map(x => {
return x.name
}))
if(findUnique.size < myArray.length){
console.log("duplicates found!")
}else{
console.log("Done!")
}
const duplicateValues = [{ name: "abc" }, { name: "bcv" }, { name: "abc" }];
const isContainDuplicate = (params) => {
const removedDuplicate = new Set(params.map((el) => el.name));
return params.length !== removedDuplicate.size;
};
const isDuplicate = isContainDuplicate(duplicateValues);
console.log("isDuplicate");