Sort javascript array by multiple keys - javascript

I have an array of objects something like this
var itemArray = [
{
"name": "name1",
"flag": true,
},
{
"name": "name1",
"flag": false,
},
{
"name": "name2",
"flag": false,
},
{
"name": "name3",
"flag": true,
}
];
I am already sorting this by the name key using the following:
var sortedItems = sortByKey(itemArray, "name");
function sortByKey(array, key) {
return array.sort(function(a, b) {
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
How do i also sort it by the flag key alphabetically so the false flags appear before the true flags? (I can use a string instead of boolean on the flag property if this makes it simpler)
To clarify, what I am looking for is an array which would be returned like this for example:
[
{ name: "name_a", flag: false },
{ name: "name_a", flag: true },
{ name: "name_b", flag: false },
{ name: "name_b", flag: true},
{ name: "name_c", flag: true},
{ name: "name_d", flag: false},
]

var sortedItems = sortByKey(itemArray, "name");
function sortByKey(array, key) {
return array.sort(function(a, b) {
if(a[key] == b[key]) {
return (a['flag'] ? -1 : 1);
} else {
var x = a[key];
var y = b[key];
return x.localeCompare(y);
}
});

var sortedItems = sortByKey(itemArray, "name");
function sortByKey(array, key) {
return array.sort(function(a, b) {
if(a['flag'] == f['flag']) {
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
} else {
return (a['flag'] ? -1 : 1);
}
});

You could use a proper callback, sort first by flag and if equal then by name.
var itemArray = [{ "name": "name1", "flag": true, }, { "name": "name1", "flag": false, }, { "name": "name2", "flag": false, }, { "name": "name3", "flag": true, }];
itemArray.sort(function (a, b) {
return a.name.localeCompare(b.name) || a.flag - b.flag;
}),
console.log(itemArray);

Related

Sort array of object on a property but keep the objects first for which property is missing

Here is an example
testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
]
Here for the the object { "_id": "oh I would be very first" } does not have the property order so it should come first.
And then the rest of the objects should be sorted according to the property "order"
So after sorting it should be,
output= [ { _id: 'oh I would be very first' },
{ _id: 'my', order: 0 },
{ _id: 'name', order: 1 },
{ _id: 'is', order: 2 },
{ _id: 'adam', order: 3 } ]
Logic is basic array sorting logic.
If both a.order and b.order are defined return 1 or -1 depending on the largest value.
If either one of them is undefined return 1 or -1 depending on the defined value.
Please Note: The value 1 and -1 determines the relative position between the two nodes. Returning 1 places a after b and -1 places a before b.
const testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const output = testWidgetOrderSort.sort((a, b) => {
if( a.order !== undefined && b.order !== undefined ) {
return a.order > b.order ? 1 : -1;
} else {
return a.order !== undefined ? 1 : -1
}
});
console.log(output);
I came up with something like this:
const test = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const x = test.sort((a, b) => {
const [STAY, SWAP] = [-1, 1];
if (!a.hasOwnProperty('order')) { return STAY; }
if (!b.hasOwnProperty('order')) { return SWAP; }
return a.order - b.order;
});
console.log(x);
You just have to pass the custom comparator function
if (!("order" in a)) return -1;
if (!("order" in b)) return 1;
else return a.order - b.order;
1) return -1 if property order doesn't exist in a.
2) return 1 if property order doesn't exist in b.
3) if both the object has order property then just sort in ascending order.
const arr = [
{ _id: "name", order: 1 },
{ _id: "is", order: 2 },
{ _id: "my", order: 0 },
{ _id: "oh I would be very first" },
{ _id: "adam", order: 3 },
];
const result = arr.sort((a, b) => {
if (!("order" in a)) return -1;
if (!("order" in b)) return 1;
else return a.order - b.order;
});
console.log(result);
If you don't care about the performance too much, the below should be fine,
const testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const finalArr = testWidgetOrderSort.filter(a => typeof a.order === "undefined");
const sortedArrWithOrderItems = testWidgetOrderSort.filter(a => typeof a.order !== "undefined").sort((a,b) => (a.order > b.order ? 1 : -1));
finalArr.push(...sortedArrWithOrderItems);
console.log(finalArr);
Note: Personally I would recommend going with #Nitheesh or #decpk solution, it is more clean and performance wise better. My solution is just to give another solution for the problem

Sort an array of objects by multiple criteria and possible undefined values

I'm trying to sort my javascript array of objects
ownerName dogCode dogName
Bob 5 Rex
John
Alisha 3 Moon
Darren 4 Boss
Josh
Cerq
I want it to be sort first by dogCode (just if exists or not, ignoring the number), than by ownerName, and finally by dogName, like this:
ownerName dogCode dogName
Alisha 3 Moon
Bob 5 Rex
Darren 4 Boss
Cerq
John
Josh
I tried this:
data.sort(function (a, b) {
if (a.dogCode < b.dogCode || !a.dogCode) return 1;
if (a.dogCode > b.dogCode || !b.dogCode) return -1;
if (a.ownerName < b.ownerName || !a.ownerName) return 1;
if (a.ownerName > b.ownerName || !b.ownerName) return -1;
if (a.dogName < b.dogName || !a.dogName) return 1;
if (a.dogName > b.dogName || !b.dogName) return -1;
return 0;
});
Aparenttely, It is sorting by dogCode correctly, but not by name/dogName. How can I do this?
EDIT: here is my json object:
{
"data": [
{
"ownerName": "Bob",
"dogCode": "5",
"dogName": "Rex"
},
{
"ownerName": "John"
},
{
"ownerName": "Alisha",
"dogCode": "3",
"dogName": "Moon"
},
{
"ownerName": "Darren",
"dogCode": "4",
"dogName": "Bos"
},
{
"ownerName": "Josh"
},
{
"ownerName": "Cerq"
}
]
}
It may not be the most efficient way, but I find the code below to be pretty understandable. Note that the sorts need applied in the opposite order from how you listed them.
const obj = {
"data": [
{
"ownerName": "Bob",
"dogCode": "5",
"dogName": "Rex"
},
{
"ownerName": "John"
},
{
"ownerName": "Alisha",
"dogCode": "3",
"dogName": "Moon"
},
{
"ownerName": "Darren",
"dogCode": "4",
"dogName": "Bos"
},
{
"ownerName": "Josh"
},
{
"ownerName": "Cerq"
}
]
};
const byPropExists = prop => (a, b) => {
if (a[prop] !== undefined && b[prop] === undefined) return -1;
if (a[prop] === undefined && b[prop] !== undefined) return 1;
return 0;
}
const byPropValue = prop => (a, b) => a[prop]?.localeCompare(b[prop])
// Note the reverse order of your sorts
console.log(obj.data
.sort(byPropValue('dogName'))
.sort(byPropValue('ownerName'))
.sort(byPropExists('dogCode'))
);
You could take the delta of the check for undefined as sorting value and sort by delta of numbers or with String#localeCompare for strings.
const data = [{ ownerName: 'Bob', dogCode: 5, dogName: 'Rex' }, { ownerName: 'John' }, { ownerName: 'Alisha', dogCode: 3, dogName: 'Moon' }, { ownerName: 'Darren', dogCode: 4, dogName: 'Boss' }, { ownerName: 'Josh' }, { ownerName: 'Cerq' }];
data.sort((a, b) =>
(a.dogCode === undefined) - (b.dogCode === undefined) ||
(a.ownerName === undefined) - (b.ownerName === undefined) ||
(a.dogName === undefined) - (b.dogName === undefined) ||
(a.ownerName || '').localeCompare(b.ownerName || '') ||
a.dogCode - b.dogCode ||
(a.dogName || '').localeCompare(b.dogName || '')
);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to convert JSON to list of key with dotnotation

I am converting JSON keys to the list with dot-notation. If any dot is there represent nested jsonobject and if any [](array notation) is there resents jsonarray.
var keyify = (obj, prefix = '') =>
Object.keys(obj).reduce((res, el) => {
if (Array.isArray(obj[el])) {
return [...res, ...keyify(obj[el][0], prefix + el + '[].')];
} else if (typeof obj[el] === 'object' && obj[el] !== null) {
return [...res, ...keyify(obj[el], prefix + el + '.')];
} else {
return [...res, prefix + el];
}
}, []);
Above is the sample code that I am using for the converion. If input is
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Output will be:
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
But the above code searches for only first JSONObject's keys in the JSONArray. But if the input is like this:
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
},
{
"a": "456"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Then in the above JSON case the code will give output :
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
So, the key a is missing only phone1 is coming in the list.So, how to get if multiple json keys are there then get keys with index of first occurence.
Expected output
[ 'input.test.phone[0].phone1',
'input.test.phone[1].a',
'customer[0].lastname',
'customer[0].firstname' ]
And if the JSONarray is value then it should be replaced by empty string.
For input:
const data = {
"input": {
"test": {
"phone": [
{
"phone1": ["123456"]
},
{
"a": ["1","2","3","4"]
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
In this case "phone1": ["123456"] and "a": ["1","2","3","4"] are Json array as values this case lis will be like:
Expected Output:
[ 'input.test.phone[0].phone1',//instead of 'input.test.phone[0].phone1[0]'
'input.test.phone[1].a',//instead of 'input.test.phone[1].a[0]','input.test.phone[1].a[1]','input.test.phone[1].a[2]','input.test.phone[1].a[3]',
'customer[0].lastname',
'customer[0].firstname' ]
In the above case jsonarray should be considered as value not key.
You could use for...in loop to create recursive function for this and check if the current data input is an array or not to add dot or square brackets.
const data = { "input": { "test": { "phone": [ { "phone1": ["123456"] }, { "a": ["1","2","3","4"] } ] } }, "customer": [ { "lastname": "def", "firstname": "abc" } ] }
function parse(data, prev = '') {
const result = []
const check = data => {
if (typeof data !== 'object') {
return false
}
if (Array.isArray(data)) {
if (data.some(e => (typeof e != 'object'))) {
return false
}
}
return true;
}
for (let i in data) {
let dot = prev ? '.' : ''
let str = Array.isArray(data) ? `[${i}]` : dot + i
let key = prev + str;
if (check(data[i])) {
result.push(...parse(data[i], key))
} else {
result.push(key)
}
}
return result
}
const result = parse(data);
console.log(result)
You can traverse through the scope of the object and capture any paths that have a non-object value.
This is an extremely uncoupled and generic soulution.
const traverse = (obj, visitorFn, scope = []) => {
for (let key in obj) {
visitorFn.apply(this, [key, obj[key], scope]);
if (obj[key] !== null && typeof obj[key] === 'object') {
traverse(obj[key], visitorFn, scope.concat(key));
}
}
}
const scopeToPath = (obj) => obj.reduce((path, key) =>
path + (!isNaN(key) ? `[${key}]` : `.${key}`), '').substring(1);
const findObjectPaths = (obj) => {
let paths = [];
traverse(obj, (key, value, scope) => {
if (typeof value !== 'object') {
paths.push(scopeToPath(scope.concat(key)));
}
});
return paths;
};
console.log(findObjectPaths(getData()));
function getData() {
return {
"input": {
"test": {
"phone": [{ "phone1": "123" }, { "a": "456" }]
}
},
"customer": [{ "lastname": "def", "firstname": "abc" }]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
You could take a nested approach by having a look to the types of the object.
function flat(object, keys = '') {
if (!object || typeof object !== 'object') return [keys];
if (Array.isArray(object))
return object.every(o => !o|| typeof o!== 'object')
? [keys]
: object.flatMap((o, i, { length }) =>
flat(o, `${keys}[${length === 1 ? '' : i}]`));
return Object
.entries(object)
.flatMap(([k, v]) => flat(v, `${keys}${keys && '.'}${k}`));
}
var data = { input: { test: { phone: [{ phone1: ["123456"] }, { a: ["1", "2", "3", "4"] }] } }, customer: [{ lastname: "def", firstname: "abc" }] },
result = flat(data);
console.log(result);

sorting by ascending and descending [duplicate]

This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 3 years ago.
I am trying to sort the array of objects based on change property.
Below is the sort function, the strange part is the price when below 100 it's not getting sorted properly. I want the array of objects to be sorted by asc or desc order by either change or name.
const data = [{
"id": 74368,
"account": "Gerald Wehner",
"change": "186.00"
},
{
"id": 55998,
"account": "Augusta Koelpin",
"change": "277.00"
},
{
"id": 3044,
"account": "Austyn Bradtke",
"change": "473.00"
},
{
"id": 50305,
"account": "Lesly Boyer",
"change": "56.00"
},
{
"id": 20324,
"account": "Marietta Lynch",
"change": "707.00"
},
{
"id": 40233,
"account": "Eriberto Haley",
"change": "923.00"
}
];
sort = (arr, field, order, cond) => {
const fn = cond ?
function(x) {
return cond(x[field])
} :
function(x) {
return x[field]
};
order = !order ? 1 : -1;
return arr.sort(function(a, b) {
return a = fn(a), b = fn(b), order * ((a > b) - (b > a));
})
}
console.log(sort(data, 'change', true, false))
You're comparing the values as text instead of numbers.
const data = [{
"id": 74368,
"account": "Gerald Wehner",
"change": "186.00"
},
{
"id": 55998,
"account": "Augusta Koelpin",
"change": "277.00"
},
{
"id": 3044,
"account": "Austyn Bradtke",
"change": "473.00"
},
{
"id": 50305,
"account": "Lesly Boyer",
"change": "56.00"
},
{
"id": 20324,
"account": "Marietta Lynch",
"change": "707.00"
},
{
"id": 40233,
"account": "Eriberto Haley",
"change": "923.00"
}
];
sort = (arr, field, order, cond) => {
const fn = cond ?
function(x) {
return Number(cond(x[field]));
} :
function(x) {
return Number(x[field]);
};
order = !order ? 1 : -1;
return arr.sort(function(a, b) {
return a = fn(a), b = fn(b), order * ((a > b) - (b > a));
})
}
console.log(sort(data, 'change', true, false))
Note that, in the case of strings like "account" this sorts by first name, then last name:
const data=[{id:74368,account:"Gerald Wehner",change:"186.00"},{id:55998,account:"Augusta Koelpin",change:"277.00"},{id:3044,account:"Austyn Bradtke",change:"473.00"},{id:50305,account:"Lesly Boyer",change:"56.00"},{id:20324,account:"Marietta Lynch",change:"707.00"},{id:40233,account:"Eriberto Haley",change:"923.00"}]
function sortData(dir, prop){
return data.sort((a,b) => {
let x = dir === "ASC" ? a : b
let y = dir === "ASC" ? b : a
if(isNaN(data[0][prop])){
return x[prop].localeCompare(y[prop])
}else{
return x[prop] - y[prop]
}
})
}
console.log(sortData("ASC", "change"))
Update
Added functionality for new format (see comments)
const data=[{id:74368,account:"Gerald Wehner",change:" GeraldWehner - 186"},{id:55998,account:"AugustaKoelpin",change:"AugustaKoelpin - 999"}]
function sortData(dir, prop){
return data.sort((a,b) => {
let x = dir === "ASC" ? a : b
let y = dir === "ASC" ? b : a
let exProp = data[0][prop]
if(isNaN(exProp)){
if( exProp.indexOf("-") > -1 && !isNaN( exProp.split("-")[1].trim() ) ){
let xTest = x[prop].split("-")[1].trim()
let yTest = y[prop].split("-")[1].trim()
return xTest - yTest
}
return x[prop].localeCompare(y[prop])
}else{
return x[prop] - y[prop]
}
})
}
console.log(sortData("ASC", "change"))

Unable to find whether an object exists in an Array Of Objects given object as an input

I am a junior level developer trying to solve a scenario, where i should get an error if we are trying to add an existing object to the array.
Input :
{
id: "0",
name: "sdsd"
}
Existing Arrays:
[{
id: "0",
name: "sdsd"
},
{
id: "1",
name: "sds"
},
{
id: "2",
name: "sdf"
}]
I am expecting the function to be something like
findDuplicate(Array, Object) => return true if there is one, else false.
use the some method, as it will return true / false. If some of the items match true is returned otherwise it will return false if nothing matches (your callback must return a truthy/falsey value).
let items = [
{ id: "0", name: "sdsd" },
{ id: "1", name: "sds" },
{ id: "2", name: "sdf" }
]
// Test "name"
console.log(items.some(i => i.name == 'sdsd'))
console.log(items.some(i => i.name == 'dog'))
// Test "id"
console.log(items.some(i => i.id == '0'))
console.log(items.some(i => i.id == '100'))
// Test both "name" and "id"
console.log(items.some(i => i.name == 'sdsd' && i.id == '0'))
console.log(items.some(i => i.name == 'sdsd' && i.id == '100'))
You can use array filter and array every like follow:
var exist = [{
id: "0",
name: "sdsd"
},
{
id: "1",
name: "sds"
},
{
id: "2",
name: "sdf"
}];
function findDuplicate(array, object){
var result = array.filter(current=>{
return Object.keys(current).every(key =>{
if (object[key] === current[key]){
return key;
}
});
});
//console.log(result);
if (result.length == 0 )
return false;
return true;
}
console.log (findDuplicate(exist,{id:"0",name:"sdsdf"}));
console.log (findDuplicate(exist,{id:"0",name:"sdsd"}));

Categories