Sort() with non-existent values - javascript

I know that undefined values should be sent to the end of the result, but what about non-existent keys? (Shouldn't be the same?) It seems sort doesn't work in those cases:
const names = [
{
name: "John",
age: 27
},{
name: "Charles",
},{
name: "Ellen",
age: 30
},{
name: "Mario",
},
{
name: "Emanuelle",
age: 18
}
]
names.sort(function (a, b) {
if (a.age > b.age) return 1;
if (a.age < b.age) return -1;
return 0;
})
console.log(names) // Sort not working, prints original order
Ideally I want to modify the "names" array and not create/reassign more variables.

Your default sort solution is set to keep the item in its current position --> return 0. You could provide another conditional that captures undefined and return -1
const
names = [{ name: "John", age: 27 }, { name: "Charles" }, { name: "Ellen", age: 30 }, { name: "Mario" }, { name: "Emanuelle", age: 18 }];
names.sort(function (a, b) {
if(b.age === undefined) return -1;
if (a.age > b.age) return 1;
if (a.age < b.age) return -1;
return 0;
})
console.log(names) // Sort not working, prints original order

So check if the age is defined. If not set it to a large number to force it to the end.
const names = [
{
name: "John",
age: 27
},{
name: "Charles",
},{
name: "Ellen",
age: 30
},{
name: "Mario",
},
{
name: "Emanuelle",
age: 18
}
]
function getAge (obj) {
return obj.age === undefined ? Number.MAX_VALUE : obj.age;
}
names.sort(function (a, b) {
// if (a.age === b.age) {
// return a.name.localeCompare(b.name);
// }
return getAge(a) - getAge(b);
})
console.log(names);

You could check for the property and if not exist, move this objects to bottom. for the rest sort by age.
const
names = [{ name: "John", age: 27 }, { name: "Charles" }, { name: "Ellen", age: 30 }, { name: "Mario" }, { name: "Emanuelle", age: 18 }];
names.sort((a, b) =>
('age' in b) - ('age' in a) || // sort object without `age` to bottom
a.age - b.age // sort by `age`
);
console.log(names);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sort not work properly because any number operation with undefined are NaN I.E: 1 - undefined = NaN.
For this sort, you can use destructuring + defaults, try this:
const names = [
{
name: "John",
age: 27
},{
name: "Charles",
},{
name: "Ellen",
age: 30
},{
name: "Mario",
},
{
name: "Emanuelle",
age: 18
}
]
names.sort(
({ age: agea = Number.MAX_VALUE }, { age: ageb = Number.MAX_VALUE }) =>
agea - ageb
);
console.log(names)

Related

Find the difference between two complex objects containing array of objects

let baseObj = {
place: {city: 'Bangalore', pin: 123456},
office: [
{ name: 'Tom', age: 22, salutation: { title: 'Mr'}},
{ name: 'John', age: 31, salutation: { title: 'Mr'}}
]
}
let updatedObj = {
place: {city: 'Bangalore', pin: 99999},
office: [
{ name: 'Tom', age: 22, salutation: { title: 'Mr'}},
{ name: 'Peter', age: 16, salutation: { title: 'Mr'}},
{ name: 'John', age: 31, salutation: { title: 'Mr'}}
]
}
expected result = {
place: {city: 'Bangalore', pin: 99999},
office: [
{ name: 'Peter', age: 16, salutation: { title: 'Mr'}}
]
}
Note: comparison can be done by finding the properties of object and values but no comparison should be done hardcoding the properties
tried comparing the object but when we have an array of object i.e office, comparing with index(i.e 0,1) doesn't help as the array might not be sorted so couldn't proceed much
have tried the below code but it fails to get the desired output if the objects in an array are in different sequence as compared to the other array
ex. office1: [
{ name: 'Tom', age: 22, salutation: { title: 'Mr'}},
{ name: 'John', age: 31, salutation: { title: 'Mr'}}
]
office2: [
{ name: 'Tom', age: 22, salutation: { title: 'Mr'}},
{ name: 'Peter', age: 16, salutation: { title: 'Mr'}},
{ name: 'John', age: 31, salutation: { title: 'Mr'}}
]
function findDiff(obj1, obj2) {
var diffObj = Array.isArray(obj2) ? [] : {}
Object.getOwnPropertyNames(obj2).forEach(function(prop) {
if(prop !=='lenght' ){
if (typeof obj2[prop] === 'object') {
diffObj[prop] = obj1[prop]== undefined? obj2[prop]: findDiff(obj1[prop], obj2[prop])
if (Array.isArray(diffObj[prop]) && Object.getOwnPropertyNames(diffObj[prop]).length === 1 || Object.getOwnPropertyNames(diffObj[prop]).length === 0) {
delete diffObj[prop]
}
}} else if(prop !=='lenght') {
if(obj1[prop] !== obj2[prop]){
diffObj[prop] = obj2[prop]
}
}
});
return diffObj
}
This compare function seems to achieve exactly what you want :
const baseObj = {"grade":"A","queue":"1","myCollections":{"myCollection":[{"commonCollection":[{"winterCollection":[{"name":"ICE","isChilled":"true"}]}]}]},"remarks":{"remark":[{"name":"GOOD","category":"A","text":{"value":"Very Good"},"indicator":"good"}]}}
const updatedObj = {"grade":"A","queue":"1","myCollections":{"myCollection":[{"commonCollection":[{"winterCollection":[{"name":"ICE","isChilled":"true"},{"code":"SNOW","isChilled":"true"}]}]}]},"remarks":{"remark":[{"name":"GOOD","category":"A","text":{"value":"Very Good"},"indicator":"good"},{"name":"BEST","text":{"value":"Test"},"category":"O","indicator":"outstanding"}]}}
const compare = (a, b, allObj = true) => {
if (typeof a !== 'object') return a === b ? null : b
if (Array.isArray(a)) {
const arr = []
b.forEach(e => {
if (a.every(el => compare(el, e, true) !== null))
arr.push(e)
});
return arr.length === 0 ? null : arr
} else {
const keys = Object.keys(b) // assuming a and b have the same properties
const obj = allObj ? b : {}
let changed = false
keys.map(key => {
const compared = compare(a[key], b[key], true)
if (compared) {
obj[key] = compared
changed = true
}
})
return changed ? obj : null
}
}
const expectedResult = {"grade":"A","queue":"1","myCollections":{"myCollection":[{"commonCollection":[{"winterCollection":[{"code":"SNOW","isChilled":"true"}]}]}]},"remarks":{"remark":[{"name":"BEST","text":{"value":"Test"},"category":"O","indicator":"outstanding"}]}}
const result = compare(baseObj, updatedObj)
console.log(result)
console.log(expectedResult)
console.log(JSON.stringify(result) === JSON.stringify(expectedResult))
PS: I compared each pair but it is O(n^2). The best way is if you had an id property on every of array children.

Counting the occurrences of an object in an array of objects based on a key [duplicate]

This question already has answers here:
Group array of objects by value and get count of the groups
(2 answers)
Closed last year.
I have an array of objects as follows :
let people = [
{ name: "Emily", age: 15 }, { name: "Emma", age: 16 },
{ name: "Stacy", age: 18 }, { name: "Emily", age: 15 },
{ name: "Jennifer", age: 12 }
];
I need to return the result containing the age as the key and frequency as it's corresponding value as follows :
{ 15 : 2, 16 : 1, 18 : 1, 12 : 1 }
I wish I could implement this using both forEach() and reduce().
Using Array#reduce:
const people = [ { name: "Emily", age: 15 }, { name: "Emma", age: 16 }, { name: "Stacy", age: 18 }, { name: "Emily", age: 15 }, { name: "Jennifer", age: 12 } ];
const ageCount = people.reduce((map, { age }) => ({
...map,
[age]: (map[age] || 0) + 1
}), {});
console.log(ageCount);

Scalable table sorting approach

I want to sort each column in react. I can do it, but forcing the code, for example if I have this array and display it in a html table, I want when I click id it sort in ascending order, when I click name it sort in ascending order and when click again it sort descending order, i want the same with name and age. And at the same time i want an arrow that if that column is in ascendig is looking up otherwise is looking down.
const USERS = [
{ id: 1, name: "Andy", age: 32 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Tom Hulk", age: 40 },
{ id: 4, name: "Tom Hank", age: 50 },
{ id: 5, name: "Audra", age: 30 },
{ id: 6, name: "Anna", age: 68 },
{ id: 7, name: "Tom", age: 34 },
{ id: 8, name: "Tom Riddle", age: 28 },
{ id: 9, name: "Bolo", age: 23 },
];
The way that i can do it is like this, but it's ugly and not practical. And also, when i change the sort state for the arrow, it updates for all the arrow not for the arrow of the column that i clicked
const sortBy = (k) => {
if (k === "id") {
if (sort) {
const res = USERS.sort((a, b) => (a.id > b.id ? 1 : -1));
setDataFilter(res);
setSort(!sort);
console.log(res);
} else {
const res = USERS.sort((a, b) => (a.id < b.id ? 1 : -1));
setDataFilter(res);
setSort(!sort);
console.log(res);
}
} else if (k === "age") {
if (sort) {
const res = USERS.sort((a, b) => (a.age > b.age ? 1 : -1));
setDataFilter(res);
setSort(!sort);
console.log(res);
} else {
const res = USERS.sort((a, b) => (a.age < b.agek ? 1 : -1));
setDataFilter(res);
setSort(!sort);
console.log(res);
}
} else if (k === "name") {
if (sort) {
const res = USERS.sort((a, b) => a.name.localeCompare(b.name));
setDataFilter(res);
setSort(!sort);
console.log(res);
} else {
const res = USERS.sort((a, b) => b.name.localeCompare(a.name));
setDataFilter(res);
setSort(!sort);
console.log(res);
}
} else {
console.log("hmm");
}
};
Try the below code. It checks the type of the sorting key value and choose the sorting method accordingly. I re-used your logic to make it simple for you.
const USERS = [
{ id: 1, name: "Andy", age: 32 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Tom Hulk", age: 40 },
{ id: 4, name: "Tom Hank", age: 50 },
{ id: 5, name: "Audra", age: 30 },
{ id: 6, name: "Anna", age: 68 },
{ id: 7, name: "Tom", age: 34 },
{ id: 8, name: "Tom Riddle", age: 28 },
{ id: 9, name: "Bolo", age: 23 },
];
const sortBy = (k, sort) => {
var res;
if( USERS[0] && (typeof USERS[0][k] == 'string')) {
res = sort ? USERS.sort((a, b) => a[k].localeCompare(b[k])) : USERS.sort((a, b) => b[k].localeCompare(a[k]));
} else {
const f = sort ? 1 : -1;
res = USERS.sort((a, b) => (a[k] > b[k] ? f : -f));
}
// setDataFilter(res);
// setSort(!sort);
console.log(res);
}
sortBy('name', 1);
sortBy('age', 1);
sortBy('name', 0);
sortBy('age', 0);
You may want to have sorting callbacks within a mapping object (by property name or by property type).
Also, do not forget to leverage useMemo()/ useCallback() hooks to boost sorting performance through memoizing the sorting output (which may be beneficial for large number of items):
const users = [
{ id: 1, name: "Andy", age: 32 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Tom Hulk", age: 40 },
{ id: 4, name: "Tom Hank", age: 50 },
{ id: 5, name: "Audra", age: 30 },
{ id: 6, name: "Anna", age: 68 },
{ id: 7, name: "Tom", age: 34 },
{ id: 8, name: "Tom Riddle", age: 28 },
{ id: 9, name: "Bolo", age: 23 },
],
sortDirection = {
'asc': 1,
'desc': -1
},
numericSorter = propName =>
sortOrder =>
({[propName]:a}, {[propName]:b}) =>
(a-b)*sortDirection[sortOrder],
stringicSorter = propName =>
sortOrder =>
({[propName]:a}, {[propName]:b}) =>
a.localeCompare(b)*sortDirection[sortOrder],
sortMap = {
id: numericSorter('id'),
name: stringicSorter('name'),
age: numericSorter('age')
},
sortUsers = (users, byProp, sortOrder) =>
users.sort(sortMap[byProp](sortOrder))
console.log(sortUsers(users, 'name', 'desc'))
console.log(sortUsers(users, 'age', 'asc'))
.as-console-wrapper{min-height: 100%;}

Javascript Map Array Object changes original array when try to add property

I map array of objects and try to add property and return new array and log it. But turned out it changes the original array too... Why does this happen? I believe it is some sort of object trick.
var students = [
{ name: 'nika', age: 25 },
{ name: 'goga', age: 11 },
{ name: 'saba', age: 20 },
{ name: 'tedo', age: 35 },
{ name: 'gio', age: 15 },
{ name: 'lasha', age: 5 },
{ name: 'sandro', age: 8 },
];
function solution(arr) {
let newArr = arr.map(function (item) {
if (item.age < 18) {
item.forbidden = true;
}
return item;
});
console.log(newArr);
console.log(students);
}
solution(students);
I want solution in ES5
You could create a copy from the object which does not share the same object reference.
function solution(arr) {
return arr.map(function (item) {
item = JSON.parse(JSON.stringify(item));
if (item.age < 18) {
item.forbidden = true;
}
return item;
});
}
var students = [
{ name: 'nika', age: 25 },
{ name: 'goga', age: 11 },
{ name: 'saba', age: 20 },
{ name: 'tedo', age: 35 },
{ name: 'gio', age: 15 },
{ name: 'lasha', age: 5 },
{ name: 'sandro', age: 8 },
];
console.log(solution(students));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz correctly points out that a solution is to create a copy of each object within the function in the map method and alludes to why this is necessary: The map method is creating a new array of objects, but those objects share the same references as the objects in the original array. Thus, any changes made to the objects reflects in both arrays.

How to find the value inside an array with objects with the most occuring value (deep mode)?

Let's say I have an array with objects like this:
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
How do I find the value based on a key with the most occuring value?
In this example, when passing name as key that would be Erik.
Something like this:
const deepMode = (array, key) => {
// Perhaps something with .reduce?
}
And when called, it should return:
deepMode(persons, "name"); // Returns "Erik"
You could take a Map, count the ocurrences and reduce the key/value pairs for getting the max valaue. Return the key without iterating again.
const
deepMode = (array, key) => Array
.from(array.reduce((m, o) => m.set(o[key], (m.get(o[key]) || 0) + 1), new Map))
.reduce((a, b) => a[1] > b[1] ? a : b)[0],
persons = [{ name: "Erik", age: 45 }, { name: "Bob", age: 37 }, { name: "Erik", age: 28 }, { name: "Jasper", age: 29 }, { name: "Erik", age: 34 }];
console.log(deepMode(persons, 'name'));
you can count keys by adding them in object and checking if key exists in object,then increment value, if not then add key into object, after that with Object.keys get keys of object sort them and get first element which is most occurring
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
const deepMode = (array, key) => {
const obj = {};
array.forEach(v => {
if (obj[v[key]]) {
obj[v[key]] += 1;
} else {
obj[v[key]] = 1;
}
});
return Object.keys(obj).sort((a,b) => obj[b]-obj[a])[0];
}
console.log(deepMode(persons, 'name'));
You could reduce into a Map, find the max, then find and return it.
function findMostOccuringKeyValue(arr, key) {
const grouped = arr.reduce((a, b) => a.set(b[key], (a.get(b[key]) || 0) + 1), new Map);
const max = Math.max(...grouped.values());
return [...grouped].find(([k, v]) => v === max)[0];
}
console.log(findMostOccuringKeyValue(persons, 'name'));
<script>
const persons = [
{
name: "Erik",
age: 45
},
{
name: "Bob",
age: 37
},
{
name: "Erik",
age: 28
},
{
name: "Jasper",
age: 29
},
{
name: "Erik",
age: 34
}
];
</script>

Categories

Resources