This question already has answers here:
Sort Array Elements (string with numbers), natural sort
(8 answers)
Closed 11 months ago.
I am trying to arrange given values in ascending orders
const value = [
{ val: "11-1" },
{ val: "12-1b" },
{ val: "12-1a" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
I am using code below to sort this in ascending order:
value.sort((a,b)=>(a.val >b.val)? 1:((b.val>a.val)?-1:0));
The result of this sort is in the order 11-1,12-1a, 12-1b, 12-7, 12-700, 12-8. However, I want the order to be 11-1,12-1a, 12-1b, 12-7, 12-8, 12-700.
How can I achieve that?
If you're only interested of sorting by the value after the hyphen you can achieve it with this code:
const value = [
{val:'12-1'},
{val:'12-700'},
{val:'12-7'},
{val:'12-8'},
];
const sorted = value.sort((a,b) => {
const anum = parseInt(a.val.split('-')[1]);
const bnum = parseInt(b.val.split('-')[1]);
return anum - bnum;
});
console.log(sorted);
updated the answer as your question update here's the solution for this:
const value = [{ val: '11-1' }, { val: '12-1b' }, { val: '12-1a' }, { val: '12-700' }, { val: '12-7' }, { val: '12-8' }];
const sortAlphaNum = (a, b) => a.val.localeCompare(b.val, 'en', { numeric: true });
console.log(value.sort(sortAlphaNum));
You can check the length first and then do the sorting as follow:
const value = [
{ val: "12-1" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
const result = value.sort(
(a, b)=> {
if (a.val.length > b.val.length) {
return 1;
}
if (a.val.length < b.val.length) {
return -1;
}
return (a.val >b.val) ? 1 : ((b.val > a.val) ? -1 : 0)
}
);
console.log(result);
little change's to #Christian answer it will sort before and after - value
const value = [{ val: '12-1' }, { val: '12-700' }, { val: '11-7' }, { val: '12-8' }];
const sorted = value.sort((a, b) => {
const anum = parseInt(a.val.replace('-', '.'));
const bnum = parseInt(b.val.replace('-', '.'));
return anum - bnum;
});
console.log(sorted);
If you want to check for different values both before and after the hyphen and include checking for letters, the solution at the end will solve this.
Here's what I did:
Created a regex to split the characters by type:
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
Created a comparison function to take numbers and letters into account:
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
Split the values based on regexValueSplit:
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
This produces results as follows (example string "12-1a"):
[
"",
"12",
null,
"1",
"a",
""
]
Then, since all the split arrays should have the same length, compare each value in a for loop:
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
// Return 0 if all values are equal
return 0;
const value = [{
val: "11-1"
},
{
val: "12-1b"
},
{
val: "12-1a"
},
{
val: "12-700"
},
{
val: "12-7"
},
{
val: "12-8"
},
];
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
return 0;
});
console.log(JSON.stringify(value, null, 2));
Since you are sorting on string values, try using String.localeCompare for the sorting.
Try sorting on both numeric components of the string.
const arr = [
{val:'12-1'},
{val:'11-900'},
{val:'12-700'},
{val:'12-7'},
{val:'11-1'},
{val:'12-8'},
{val:'11-90'},
];
const sorter = (a, b) => {
const [a1, a2, b1, b2] = (a.val.split(`-`)
.concat(b.val.split(`-`))).map(Number);
return a1 - b1 || a2 - b2; };
console.log(`Unsorted values:\n${
JSON.stringify(arr.map(v => v.val))}`);
console.log(`Sorted values:\n${
JSON.stringify(arr.sort(sorter).map(v => v.val))}`);
Related
how to sort highest value in this data using javascript?
data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
Expected
data = [{a: [{num:31}]},{a: [{num:9}]},{a: [{num:9}]}]
I try like this but never happen :)
const data_sort = data.sort((a, b) => {
let abc
if (a.a.length > 0) {
abc = a.a.sort((x, y) => x.a - x.a);
}
return a - b
})
let data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
data = data.map(item => ({a:[item.a.sort((a, b) => b.num-a.num)[0]]})).sort((a, b) => b.a[0].num-a.a[0].num)
console.log(data)
Assuming this is the correct syntax, all you need is to map every item in the array to it's largest item, then sort that.
var data = [{
a: [{num:31}, {num:10}]
}, {
a: [{num:4}, {num:9}]
}, {
a: [{num:6}, {num:11}]
}, {
a: [{num:5}, {num:9}]
}];
var result = data.map(function(item) {
return {
a: [item.a.sort(function(a,b) {
return a.num - b.num
}).reverse()[0]]
};
}).sort(function(a, b) {
return a.a[0].num - b.a[0].num
}).reverse();
console.log(result)
.as-console-wrapper {
max-height: 100% !important;
}
Map and Reduce
const data = [{ a: [{ num: 31 }, { num: 10 }] }, { a: [{ num: 4 }, { num: 9 }] }, { a: [{ num: 5 }, { num: 9 }] }];
data.map((value) => {
value.a = [
{
num: value.a.reduce((accumulatedValue, currentValue) => {
return Math.max(accumulatedValue.num, currentValue.num);
}),
},
];
return value;
});
console.log(data)
I have an array of objects and I want to add an element to specific index when a certain attribute changes compared to the previous one.
We have:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
I want it to become
const arr = [
{ separator:true }
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ separator:true }
{ num: 3 },
{ num: 3 },
{ separator:true }
{ num: 4 },
{ separator:true }
{ num: 5 },
];
I did this:
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = arr;
getIndexes(arr).forEach((position) => result.splice(position, 0, { separator: true }));
return result
};
and it returns:
[
{ separator: true },
{ num: 1 },
{ num: 1 },
{ separator: true },
{ num: 1 },
{ separator: true },
{ separator: true },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 }
]
Maybe because of the "new" size of the array, because it is getting bigger and changes its dimension.
What do you think is the best way to solve this?
Run it through .flatMap()
const result = arr.flatMap((obj, idx, arr) => {...
.flatMap() is .map() and .flat() combined, so it transforms the contents of a copy of the given array and removes the brackets []. Next, we return the first object with a separator:
if (idx == 0) {
// returns are wrapped in brackets because they'll be removed before being returned
return [{separator: true}, obj];
}
The next step is to compare the current value with the previous value:
obj.num == arr[idx - 1].num ? // current value vs previous value
[arr[idx - 1]] : // if they are the same value return previous value
[{separator: true}, obj]; /* if they are not the same then return that separator
and current */
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const result = arr.flatMap((obj, idx, arr) => {
if (idx == 0) {
return [{
separator: true
}, obj];
}
return obj.num == arr[idx - 1].num ? [arr[idx - 1]] : [{
separator: true
}, obj];
});
console.log(JSON.stringify(result, null, 2));
I propose this solution which would consume only one iteration with a reduce :
const arr = [{
num: 1
},
{
num: 1
},
{
num: 1
},
{
num: 3
},
{
num: 3
},
{
num: 4
},
{
num: 5
},
];
let prev_value = arr[0];
const result = arr.reduce((acc, val) => {
const insert = (val.num !== prev_value.num) ? [{
separator: true
}, val] : [val];
prev_value = val;
return acc.concat(insert)
}, [{
separator: true
}, ])
console.log(result)
There must be other ways to do it too. But with a simple modification to your code it can be done. You just need to keep track of the offset with a new variable, incrementing it in the loop:
const arr = [
{ num: 1 },
{ num: 1 },
{ num: 1 },
{ num: 3 },
{ num: 3 },
{ num: 4 },
{ num: 5 },
];
const getIndexes = (myArr) => {
let indexes = [];
let previousValue = null;
myArr.forEach((el, idx) => {
if (el.num !== previousValue) {
indexes.push(idx);
previousValue = el.num;
}
});
return indexes;
};
const insertSeparator = (arr) => {
let result = [...arr];
let offset = -1;
getIndexes(arr).forEach((position) => {
offset++;
return result.splice(position+offset, 0, { separator: true });
});
return result
};
console.log(insertSeparator(arr));
Note: If you want to start with 0 you can do the increment in the .splice() itself : result.splice(position+(offset++),
const positions = [];
//arr.sort((a, b) => a.num - b.num); You can uncomment this line to ensure that the array will always sorted based on num property
arr.forEach((item, index) => {
if (index < arr.length - 1 && item.num != arr[index + 1].num) {
positions.push(index + 1);
}
});
let counter = 0;
positions.forEach((pos) => {
arr.splice(pos + counter++, 0, { separator: true });
});
console.log(arr);
You want to:
Do something which each item in a list
Want to return something other than a list of the same size.
Then I would suggest the good all-round Array.prototype.reduce() function.
const separator = {separator: true};
arr.reduce((result, item) => {
if (result.at(-1)?.num === item.num) {
return [...result, separator, item];
}
return [...result, item]
}, [])
This is (according to me) easier, cleaner and safer since it doesn't mutate variables.
Note
Array.prototype.at() is at the time of writing a new function. If you are using an ancient browser that doesn't support it you can use arr[arr.length -1] to get the last item instead.
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");
So I am trying to make a UI like this:
And I have an array of users
[{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}]
What I am trying to do is to sort the array by first letter of the name property, and add a header object before each. For example in the picture, you can see the letter A, B, I, and J as the headers.
For now, I got it working like this:
let final = []
// sort by first letter
const sortedUsers = state.test_list.sort((a, b) => a.name.localeCompare(b.name))
for (let x = 0; x < sortedUsers.length; x++) {
const user = sortedUsers[x].name
if (user.charAt(0) === 'A') {
const checkIfExists = final.findIndex((f) => f.header === 'A')
// add the header A if it doesn't exist
if (checkIfExists < 0) final.push({header: 'A'})
}
else if (user.charAt(0) === 'B') {
const checkIfExists = final.findIndex((f) => f.header === 'B')
// add the header B if it doesn't exist
if (checkIfExists < 0) final.push({header: 'B'})
}
// else if up to the letter Z
final.push(user)
}
and if I log the final array, I get:
which is correct.
My concern is that the code is very long, and I have no idea if it can be optimized or make the code smaller.
Is there any other option to do something like this? Any help would be much appreciated.
Why don't you create a collection of names, which is grouped by the first letter? You can then loop on it, and create your list. Use Array#reduce to create the grouped collection.
And then use Object#keys to iterate over the grouped collection and render your results:
let data = [{
name: 'Julia'
}, {
name: 'Ismeh'
}, {
name: 'Alison'
}, {
name: 'Andrea'
}, {
name: 'Betty'
}];
let combined = data.reduce((result, item) => {
let letter = item.name[0].toUpperCase();
if (!result[letter]) {
result[letter] = [];
}
result[letter].push(item);
return result;
}, {});
console.log(combined);
// Iterate over the result
Object.keys(combined).forEach(key => {
// key will be the first letter of the user names and
// combined[key] will be an array of user objects
console.log(key, combined[key]);
});
One thing still to do is to sort the user arrays by user name, which you can do easily using Array#sort.
Simple enough, try sorting them and then using .reduce:
const unsortedPeople = [{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}];
const sortedUsers = unsortedPeople.sort((a, b) => a.name.localeCompare(b.name))
const final = sortedUsers.reduce((finalSoFar, user) => {
const thisUserFirstChar = user.name[0];
if (finalSoFar.length === 0) addHeader();
else {
const lastUserFirstChar = finalSoFar[finalSoFar.length - 1].name[0];
if (lastUserFirstChar !== thisUserFirstChar) addHeader();
}
finalSoFar.push(user);
return finalSoFar;
function addHeader() {
finalSoFar.push({ header: thisUserFirstChar });
}
}, []);
console.log(final);
Why don't you just keep track of the current abbreviation as you loop. Then you can add a head when it changes:
var users = [{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}]
const sortedUsers = users.sort((a, b) => a.name.localeCompare(b.name))
var currentHeader
let final = sortedUsers.reduce((a, user) => {
if (currentHeader !== user.name[0]) {
currentHeader = user.name[0]
a.push({header: currentHeader})
}
a.push(user)
return a
},[])
console.log(final)
Here's one way to do it:
const users = [{name: 'Julia'}, {name: 'Ismeh'}, {name: 'Alison'}, {name: 'Andrea'}, {name: 'Betty'}];
let lastIndex;
let result = [];
users.sort((a, b) => {
return a.name > b.name;
}).forEach((user) => {
const index = user.name.charAt(0);
if (index !== lastIndex) {
result.push({
header: index
});
}
lastIndex = index;
result.push(user.name);
}, []);
console.log(result);
You can use _.orderBy(collection, [iteratees=[_.identity]], [orders]) and _.groupBy(collection, [iteratee=_.identity]) method of lodash.
This orderBy is like _.sortBy except that it allows specifying the sort orders of the iteratees to sort by. If orders is unspecified, all values are sorted in ascending order. Otherwise, specify an order of "desc" for descending or "asc" for ascending sort order of corresponding values.
groupBy will creates an object composed of keys generated from the results of running each element of collection thru iteratee. The order of grouped values is determined by the order they occur in collection. The corresponding value of each key is an array of elements responsible for generating the key. The iteratee is invoked with one argument: (value).
example
// The `_.property` iteratee shorthand.
_.groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }
// Sort by `user` in ascending order and by `age` in descending order.
_.orderBy(users, ['user', 'age'], ['asc', 'desc']);
With lodash
let myArr = [{
name: 'Julia'
}, {
name: 'Ismeh'
}, {
name: 'Andrea'
}, {
name: 'Alison'
}, {
name: 'Betty'
}];
myArr = _.orderBy(myArr, ['name'], ['asc']);
let r = _.groupBy(myArr, o => {
return o.name.charAt(0).toUpperCase();
})
console.log(r);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
IN ES5
var arr = [{
name: 'Julia'
}, {
name: 'Ismeh'
}, {
name: 'Andrea'
}, {
name: 'Alison'
}, {
name: 'Betty'
}],
fChar = '';
arr = arr.sort(function(a, b) {
a = a.name.toUpperCase(); // ignore upper and lowercase
b = b.name.toUpperCase(); // ignore upper and lowercase
return a < b ? -1 : (a > b ? 1 : 0);
}).reduce(function(r, o) {
fChar = o.name.charAt(0).toUpperCase();
if (!r[fChar]) {
r[fChar] = [];
}
r[fChar].push({
name: o.name
});
return r;
}, {});
console.log(arr);
IN ES6
const arr = [{
name: 'Julia'
}, {
name: 'Ismeh'
}, {
name: 'Andrea'
}, {
name: 'Alison'
}, {
name: 'Betty'
}];
let result = arr.sort((a, b) => {
a = a.name.toUpperCase(); // ignore upper and lowercase
b = b.name.toUpperCase(); // ignore upper and lowercase
return a < b ? -1 : (a > b ? 1 : 0);
}).reduce((r, o) => {
let fChar = o.name.charAt(0).toUpperCase();
if (!r[fChar]) {
r[fChar] = [];
}
r[fChar].push({
name: o.name
});
return r;
}, {});
console.log(result);
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");