Javascript sort method handling null values - javascript

I have a list of objects where I want to sort the objects based on a field I know I can use sort methods. When the comparing field have null values, sorting is not happening, how to fix this issue?
http://jsfiddle.net/mailtoshebin/kv8hp/
var arrOfObj = [
{
"Name": "Zak",
"Age": 25
},
{
"Name": "Adel",
"Age": 38
},
{
"Name": null,
"Age": 38
},
{
"Name": "Yori",
"Age": 28
}
];
sortArrOfObjectsByParam(arrOfObj, "Name");
alert("ASCENDING: " + arrOfObj[0].Name + ", " + arrOfObj[1].Name + ", " + arrOfObj[2].Name);
function sortArrOfObjectsByParam(arrToSort , strObjParamToSortBy ) {
if(sortAscending == undefined) sortAscending = true; // default to true
if(sortAscending) {
arrToSort.sort(function (a, b) {
return a[strObjParamToSortBy] > b[strObjParamToSortBy];
});
}
else {
arrToSort.sort(function (a, b) {
return a[strObjParamToSortBy] < b[strObjParamToSortBy];
});
}
}

you can deal with the null values inside the comp func:
arrToSort.sort(function (a, b) {
if (a[strObjParamToSortBy]==null) return 1
if (b[strObjParamToSortBy]==null) return 0
return a[strObjParamToSortBy] > b[strObjParamToSortBy];
});

Related

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"))

How to generate minimum-size Javascript object?

How can I create a minimum-sized Javascript serialization of a Javascript object? Essentially a JSON.stringify with all unnecessary quotes removed. (Only basic JSON data types need to be supported, not Dates etc.)
For example, the JSON:
{
"pi": 3.14,
"e!": 4.26
}
would become:
{pi:3.14,"e!":4.26}
Edit: The result is not valid JSON, but is valid Javascript.
Copied from https://stackoverflow.com/a/11233515/916000 and modified:
function stringify(obj_from_json) {
if (typeof obj_from_json !== "object") {
return JSON.stringify(obj_from_json);
} else {
if (Array.isArray(obj_from_json)) {
// if the array contains an object
var arr = [];
for (var i = 0, len = obj_from_json.length; i < len; i++) {
arr.push(stringify(obj_from_json[i]));
}
return "[" + arr.join(",") + "]";
} else {
var props = Object
.keys(obj_from_json)
.map(function(key) {
return (new RegExp(/^[1-9a-zA-Z_$][a-zA-Z0-9_$.]*$/).test(key) ? key : "\"" + key + "\"") + ":" + stringify(obj_from_json[key]);
}).join(",");
return "{" + props + "}";
}
}
}
console.log(stringify({
"pi": 3.14,
"e!": 4.26
}));
console.log(stringify([{
"adjacencies": [{
"nodeTo": "graphnode2",
"nodeFrom": "graphnode1",
"data": {
"$color": "#557EAA"
}
}],
"data": {
"$color": "#EBB056",
"$type": "triangle",
"$dim": 9
},
"id": "graphnode1",
"name": "graphnode1"
}, {
"adjacencies": [],
"data": {
"$color": "#EBB056",
"$type": "triangle",
"$dim": 9
},
"id": "graphnode2",
"name": "graphnode2"
}]));
console.log(stringify({1: 2}));
console.log(stringify({"000": 42}));
console.log(stringify({1.26: 42}));
Edit: Added object array support.
Edit: Fixed array conversion.

Add element for object

I need to go through a list of objects to find the element and add a new element to the root, I can scroll through the list and find the element, but I can not add to the correct level
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
a.push(element); // ERROR
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
var element = {
"children": [{"id": 6}]
};
findById(data, 5, element);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
https://jsfiddle.net/4nrsccnu/
Use Object.assign to merge the properties from the element object to the current object of the iteration.
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
Object.assign(a, element);
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
var element = {
"children": [{"id": 6}]
};
findById(data, 5, element);
console.log(data);
You cannot push, because a is an object. However, you can simply add the desired property (in your case, children), and then assign it a value (in your case, the element).
var data = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4,
"children": [
{
"id": 6
},
{
"id": 7
}
]
},
{
"id": 5
}
];
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
a.children = element; // Add property 'children'
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
// remove property name from element, as that is being added in the function
var element = [
{"id": 6}
];
findById(data, 5, element);
document.write('<pre>' + JSON.stringify(data, 0, 4) + '</pre>');
The push function is used for arrays, not to object. Check for details https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push.
If you want to add a single keypair to your object, you can use Object.keys and Object.values. Like this:
function findById(data, id, element) {
function iter(a) {
if (a.id === id) {
var key = Object.keys(element)[0];
var value = Object.values(element)[0];
a[key] = value;
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result;
}

calculate item length in outer of map

I have a json like this
var person = [{
"name": "john",
"age": 20
}, {
"name": "Samuel",
"age": 10
}, {
"name": "Jin",
"age": 10
}]
My desire output is
age 10 (2)
- Samuel
- Jin
age 20 (1)
- John
I have problem counting the total. I would do
ages.map(doList) //where ages is [10,20]
doList(age) {
persons.filter(p => p.age === age)
.map(p => {
p.name
})
}
but how do print out the length of the age group?
You might change your data structure for easier output like.
var person = [{
"name": "john",
"age": 10
}, {
"name": "Samuel",
"age": 10
}, {
"name": "Jin",
"age": 10
}];
let newArray = [];
person.forEach((p) => {
let findPreviousIndex = newArray.findIndex((itm) =>{
return itm.age == p.age;
});
if(findPreviousIndex > -1){
// previous found, push the name
newArray[findPreviousIndex].names.push(p.name);
}else{
// not found. create a new object and push it
newArray.push({
"age" : p.age,
"names" : [].concat(p.name)
})
}
});
console.log(newArray);
Now, its easy to print your grouped data and easy to find length.
Here's one way to produce the desired output, first using .reduce() to set up a result object with a property for each age that is an array of names for that age, then mapping over the properties of result to create a formatted string that gives the specific format in the question:
var person = [{
"name": "john",
"age": 20
}, {
"name": "Samuel",
"age": 10
}, {
"name": "Jin",
"age": 10
}]
var result = person.reduce((a, c) => {
(a[c.age]||(a[c.age]=[])).push(c.name)
return a
}, {})
console.log(result)
var formatted = Object.keys(result)
.map(k=>`age ${k} (${result[k].length})\n${result[k].map(n => `- ${n}`).join('\n')}`)
.join('\n\n')
console.log(formatted)
var person = [{
"name": "john",
"age": 20
}, {
"name": "Samuel",
"age": 10
}, {
"name": "Jin",
"age": 10
}];
// create a map where the key is the person's age and the value is a list of names
var ageMap = person.reduce(function(result, p) {
var key = p.age;
var name = p.name;
if (result[key]) {
result[key].push(name);
} else {
result[key] = [name];
}
return result;
}, {});
// traverse the map and print the names of people grouped by age
Object.keys(ageMap).forEach(function(key) {
var value = ageMap[key];
console.log("age " + key + " (" + value.length + ")");
value.forEach(function(name) {
console.log("- " + name);
});
console.log("");
});
JSFiddle: https://jsfiddle.net/njcms8rj/

How can one iterate with this JSON response file

As said above, I don't know how to use this kind of JSON response from my server-side php which I got by using this code echo json_encode(array_merge($outp, $outp2));
[
{
"stuid":"12",
"stuname":"Velino Meratis",
"stucourse":"BSIT",
"stustat":"0",
"stulyear":"4",
"stulog":"feb 16 2017"
},
{
"stuid":"13",
"stuname":"Alana Melker",
"stucourse":"BSCE",
"stustat":"1",
"stulyear":"5",
"stulog":"feb 16 2017"
},
{
"stuid":"12",
"cname":"InfoTech000",
"clog":"1"
},
{
"stuid":"12",
"cname":"InfoTech001",
"clog":"2"
},
{
"stuid":"12",
"cname":"C101",
"clog":"3"
},
{
"stuid":"13",
"cname":"CE000",
"clog":"4"
},
{
"stuid":"13",
"cname":"CE001",
"clog":"5"
},
{
"stuid":"13",
"cname":"C101",
"clog":"6"
}
]
If I use this code in my client side javascript
if (this.readyState == 4 && this.status == 200) {
students = JSON.parse(this.responseText);
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.cname + "<br>";
});
}
it just ends up giving me this:-
12 Velino Meratis
undefined
13 Alana Melker
undefined
Somehow I can iterate the stuid and the stunamebut it won't allow them to contain the cname as an array with them.
How can I turn that into something like this:-
12 Velino Meratis
InfoTech000, InfoTech001, C101
13 Alana Melker
CE000, CE001, C101
Can someone Help and Elaborate on this?
You can check if the array contains the key or not, that way you will be saved from "undefined" error.
You can do it this way
if(item.hasOwnProperty('cname'))
{
console.log(item.cname);
}
You can use this for stuname or other keys also.
You need to merge student objects in your array by unique IDs. One way to do this is to add new stuids to an array and merge it with subsequent items with same stuid. Once you have array of unique students, you can proceed with other goals.
if (this.readyState == 4 && this.status == 200) {
var students_raw = JSON.parse(this.responseText);
var students = [];
students_raw.forEach(function(item){
var existing = students.find($item => item.stuid === $item.stuid);
if (existing) {
existing = Object.assign({}, existing, item);
} else {
students.push(item);
}
});
// your print loop
students.forEach(function(item) {
var x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.cname + "<br>";
});
}
Please note: Array.reduce() and Object.assign() are not supported widely. you may need to polyfill these methods
NOTE: This answer assumes you're willing and able to change the data coming from PHP
Credit where credit is due, this is an extension of #Magnus Eriksson's comment.
It would be preferable to keep the relevant data associated with each other. You should get better flexibility with well-formed data. Ideally, you should do this server side and present the data to the client already well formatted.
You should try and achieve something similar to the following for your output:
[{
"stuid": "12",
"stuname": "Velino Meratis",
"stucourse": "BSIT",
"stustat": "0",
"stulyear": "4",
"stulog": "feb 16 2017",
"classes": [{
"cname": "InfoTech000",
"clog": "1"
}, {
"cname": "InfoTech001",
"clog": "2"
}, {
"cname": "C101",
"clog": "3"
}]
}, {
"stuid": "13",
"stuname": "Alana Melker",
"stucourse": "BSCE",
"stustat": "1",
"stulyear": "5",
"stulog": "feb 16 2017",
"classes": [{
"cname": "CE000",
"clog": "4"
}, {
"cname": "CE001",
"clog": "5"
}, {
"cname": "C101",
"clog": "6"
}]
}];
Note I've used classes as the property name as it appears we are working with Courses and their classes.
Your javascript will now look like:
if (this.readyState == 4 && this.status == 200) {
students = JSON.parse(this.responseText);
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.classes.map(function(elem){
return elem.cname;}).join(",") + "<br>";
});
}
Note the map function won't work in IE8 and lower.
Now for a working example:
var students = [{
"stuid": "12",
"stuname": "Velino Meratis",
"stucourse": "BSIT",
"stustat": "0",
"stulyear": "4",
"stulog": "feb 16 2017",
"classes": [{
"cname": "InfoTech000",
"clog": "1"
}, {
"cname": "InfoTech001",
"clog": "2"
}, {
"cname": "C101",
"clog": "3"
}]
}, {
"stuid": "13",
"stuname": "Alana Melker",
"stucourse": "BSCE",
"stustat": "1",
"stulyear": "5",
"stulog": "feb 16 2017",
"classes": [{
"cname": "CE000",
"clog": "4"
}, {
"cname": "CE001",
"clog": "5"
}, {
"cname": "C101",
"clog": "6"
}]
}];
students.forEach(function(item){
console.log(item.stuid);
x = item.stuid;
document.getElementById("demo").innerHTML += x + " " + item.stuname + "<br>" + item.classes.map(function(elem){
return elem.cname;}).join(",") + "<br>";
});
<div id="demo"></div>
As mentioned in other answers and comments, the issue with your existing code, is not all objects in your json array have the property you're referencing.
You can try like this once also
HTML
<div id="result"></div>
SCRIPT
var dataJSON = [
{
"stuid":"12",
"stuname":"Velino Meratis",
"stucourse":"BSIT",
"stustat":"0",
"stulyear":"4",
"stulog":"feb 16 2017"
},
{
"stuid":"13",
"stuname":"Alana Melker",
"stucourse":"BSCE",
"stustat":"1",
"stulyear":"5",
"stulog":"feb 16 2017"
},
{
"stuid":"12",
"cname":"InfoTech000",
"clog":"1"
},
{
"stuid":"12",
"cname":"InfoTech001",
"clog":"2"
},
{
"stuid":"12",
"cname":"C101",
"clog":"3"
},
{
"stuid":"13",
"cname":"CE000",
"clog":"4"
},
{
"stuid":"13",
"cname":"CE001",
"clog":"5"
},
{
"stuid":"13",
"cname":"C101",
"clog":"6"
}
] ;
var arr = {};
for(var i = 0 ; i< dataJSON.length ; i++){
var ele = dataJSON[i];
if(arr[ ele.stuid ]==undefined){
arr[ ele.stuid ] = {};
}
arr[ ele.stuid ]['stuid'] = ele.stuid;
if(ele.stuname!=undefined){
arr[ ele.stuid ]['stuname'] = ele.stuname;
}
if(arr[ ele.stuid ]['cname'] == undefined){
arr[ ele.stuid ]['cname'] = [];
}
if(ele.cname!=undefined){
arr[ ele.stuid ]['cname'].push(ele.cname);
}
}
var str = '';
for(var key in arr){
var obj = arr[key];
str += obj['stuid'] +" "+obj['stuname']+'<br/>';
str += obj['cname'].toString()+'<br/>';
}
document.getElementById("result").innerHTML = str;
Thanks,

Categories