Sorting an array - passing a compare function - javascript

I have the following code:
var compare = function( nodeA, nodeB ){
return +nodeA.index - +nodeB.index;
};
var sort = function( nodes ){
nodes.sort( compare );
};
The node has this (pseudo) structure:
{
index: <integer>
value: <literal>
}
And it currently sorts them the regular way, when I call the sort function, and print out the index's of each node:
0
1
2
3
How can I change my current logic to make it look like this? :
1
2
3
0 <-- 0 should be considered the biggest index

You can add special handling for zeroes:
var compare = function(nodeA, nodeB) {
// in case both sides are equal
if (nodeA.index === nodeB.index) {
return 0;
}
if (nodeA.index === 0) {
return 1;
}
if (nodeB.index === 0) {
return -1;
}
return +nodeA.index - +nodeB.index;
};
var data = [{
index: 2,
value: 'a'
}, {
index: 0,
value: 'b'
}, {
index: 3,
value: 'c'
}, {
index: 1,
value: 'd'
}]
data.sort(compare);
console.log(data);

You can first sort by condition that index != 0 and then just sort by index value.
var data = [{
index: 2,
value: 'a'
}, {
index: 0,
value: 'b'
},{
index: 3,
value: 'c'
},{
index: 1,
value: 'd'
}]
var result = data.sort(function(a, b) {
return (b.index != 0) - (a.index != 0) || (a.index - b.index)
})
console.log(result)

You just need to change your compare function a little bit:
var compare = function( nodeA, nodeB ){
if (!+nodeA.index) return 1;
if (!+nodeB.index) return -1;
return +nodeA.index - +nodeB.index;
};

Just change compare to:
var compare = function( nodeA, nodeB ){
return ((+nodeA.index || Infinity) - (+nodeB.index || Infinity)) || Infinity;
};
|| operator returns the first value that is not "falsy", which is a "truthy" value, but is also the actual value. This is a EMCA5 "trick" to create default values to variables.
So explaining:
for nodeA.index == 0 && nodeB.index > 0 => Infinity - somevalue == Infinity
for nodeA.index > 0 && nodeB.index == 0 => somevalue - Infinity == -Infinity
for nodeA.index == 0 && nodeB.index == 0 => Infinity - Infinity == NaN in which case the || Infinity option is choosen

Related

How do I filter data with proper algorithm?

So I have that blocks of code:
const stops = {
mainCheckBox : true,
checkBox: {
0: true,
1: true,
2: true,
3: true
}
}
const tickets = [
{
segments: [
{
stops: ['QWE', 'RTY', 'BGT']
},
{
stops: ['CVB']
}
]
},
... // plus other same objects
]
What do I need? I need to filter them according to the length of stops + appropriate checkbox. For example: if I have chekcBox[0] true and legnth of the both stops arrays is 0, then I return the item.
So I wrote that algorithm:
const result = tickets.filter(item => {
if (stops.mainCheckBox) return item
if (stops.checkBox[0] && item.segments[0].stops.length === 0 && item.segments[1].stops.length === 0) return item
if (stops.checkBox[1] && item.segments[0].stops.length === 1 && item.segments[1].stops.length === 1) return item
if (stops.checkBox[2] && item.segments[0].stops.length === 2 && item.segments[1].stops.length === 2) return item
if (stops.checkBox[3] && item.segments[0].stops.length === 3 && item.segments[1].stops.length === 3) return item
})
But that code looks bad and unacceptable. So without other libraries (like lodash and etc), how can I refactor the algorithm?
You could take an array of key/indices and iterate with Array#some
const
result = tickets.filter(item =>
stops.mainCheckBox ||
Object.keys(stops.checkBox).some(i =>
stops.checkBox[i] &&
item.segments[0].stops.length === +i &&
item.segments[1].stops.length === +i
)
);

JavaScript array object issue

I am just a beginner in JavaScript, I have issue how to retrieve data from this array. Is it valid?
Here is console output of array object:
[]
0: Item {id: 0, symbol: "VBIV", boughtDate: "2018-07-22", company: "VBI Vaccines Inc."}
1: Item {id: 1, symbol: "R", boughtDate: "2018-07-22", company: "Ryder System Inc."}
2: Item {id: 2, symbol: "R", boughtDate: "2018-07-22", company: "Ryder System Inc."}
length: 3
__proto__: Array(0)
If you know the index of the object you want to retrieve you will simply use array[index].
If you do not know the index but you know the id you will use:
array.find(element => element.id === id)
For example I want to get id:2 company name.
To get this value, you would get the object at index 2 of the array using the bracket [] syntax, and get the id property from that object using the dot . syntax.
console(myArray[2].id) //=> 2
console(myArray[2].company) //=> "Ryder System Inc."
Or save the object from the array to a variable, and then get it's properties:
var myObj = myArray[2]
console(myObj.id) //=> 2
console(myObj.company) //=> "Ryder System Inc."
I glad to know if this could be faster than the existing, and match to what you wants.
Array.prototype.get = function(key, value, start = 0, revert = false, restart = true) {
let __forward = function(index, limit, array, key, value) {
let ele = null;
while (index < limit && (ele = array[index])[key] !== value) index++;
return {index: index, value: ele};
},
__backward =function(index, limit, array, key, value) {
let ele = null;
while (index > limit && (ele = array[index])[key] !== value) index--;
return {index: index, value: ele};
};
if (!(typeof key !== "string" || start < 0 || !(start < this.length) || typeof this[0] !== "object")) {
let length = this.length, result;
/* no revert: forward direction, until first match */
if (!revert) {
result = __forward(start, length, this, key, value);
if (result.index < length)
return result;
else if (restart && start > 0) {
result = __forward(0, start, this, key, value);
if (result.index < start)
return result;
}
} else {
/* revert: backward direction, until last match */
result = __backward(start, -1, this, key, value);
if (result.index > -1)
return result;
else if (restart && start < length-1) {
result = __backward(length-1, start, this, key, value, true);
if (result.index > start)
return result;
}
}
}
return {index: -1, value: null};
}
usage:
let a = [{id: 1, value: "rock"}, {id: 2, value: "roll"}, ...];
let item2 = a.get("id", 2, a.length-1, true),
val2 = item2.value,
idx2 = item2.index;
let item1 = a.get("id", 1, idx2 - 1, true),
val1 = item1.value,
idx1 = item1.index;
etc...
As a regular function:
window.OO7array_get = function(key, value, start = 0, revert = false, restart = true) {
/* same as above script, dont worry feel free to copy and paste here */
};

Sort javascript array null value always at bottom

Hey i am using slickgrid plugin and there i have function sortNumeric to sort data in order
function sorterNumeric(a, b) {
var x = (isNaN(a[sortcol]) || a[sortcol] === "" || a[sortcol] === null) ? -99e+10 : parseFloat(a[sortcol]);
var y = (isNaN(b[sortcol]) || b[sortcol] === "" || b[sortcol] === null) ? -99e+10 : parseFloat(b[sortcol]);
return sortdir * (x === y ? 0 : (x > y ? 1 : -1));
}
Can someone help me to extend this sorting, so null values comes always at last place.
You could use the result of the comparison as value for the needed delta.
In SlickGrid, you get the sort order with the property sortAsc of the wanted cols to sort. then just use the closure over the sorting direction.
function sortFn(sortAsc) {
return function (a, b) {
return (a[sortcol] === null) - (b[sortcol] === null) || (sortAsc || -1) * (a[sortcol] - b[sortcol]);
}
}
var array = [{ a: 1 }, { a: 3 }, { a: 2 }, { a: 8 }, { a: null }, { a: 42 }, { a: null }],
sortcol = 'a';
array.sort(sortFn(true)); // asc
console.log(array);
array.sort(sortFn(false)); // desc
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use following code:
var sortcol = "num";
var sortdir = -1;
function sorterNumeric(a, b) {
var x = (isNaN(a[sortcol]) || !a[sortcol]) ? 99e+10 * sortdir : parseFloat(a[sortcol]);
var y = (isNaN(b[sortcol]) || !b[sortcol]) ? 99e+10 * sortdir : parseFloat(b[sortcol]);
return x > y ? 1 * sortdir : -1 * sortdir;
}
var arr = [{ num: 1 }, { num: 3 }, { num: null }, { num: 7 }, { num: 2 } ]

Complex sort of array of objects

I have a variable and array of objects e.g:
var selectedName = 'fff';
[{
percentage: Math.round(percentage),
count: count,
name: 'bbb',
index: 11,
},
{
percentage: Math.round(percentage),
count: 200,
name: 'aaa',
index: 2,
},
{
percentage: Math.round(percentage),
count: 400,
name: 'All',
index: 7,
},
{
percentage: Math.round(percentage),
count: count,
name: 'fff',
index: 8,
},
{
percentage: Math.round(percentage),
count: count,
name: 'ccc',
index: 3,
}],
I want to sort these as follows: the object which has the name 'All' should always be first. The next object should be the one with the name that matches selectedName, in this case 'fff'. And then the rest of the objects should be ordered by ascending order of their 'index' property.
Is this possible in one Array.sort() method?
Yes, here an example:
var selectedName = 'fff';
let percentage = 1;
let count = 2;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
arr.sort((i, j) => {
if (i.name === j.name && (i.name === 'All' || i.name === selectedName)) return 0;
if (i.name === 'All') return -1;
if (j.name === 'All') return 1;
if (i.name === selectedName) return -1;
if (j.name === selectedName) return 1;
if (i.index < j.index) return -1;
if (i.index > j.index) return 1;
return 0;
})
console.log(arr)
Sure you can do it like so:
var selectedName = 'fff';
var percentage = 23;
var count = 3;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
function sortItems(a, b) {
if (a.name === 'All') {
return -1
}
if (b.name === 'All') {
return 1;
}
if (a.name === selectedName) {
return -1
}
if (b.name === selectedName) {
return 1
}
return a.index - b.index;
}
console.log(items.sort(sortItems));
You can try something like this:
Idea
(a.name !== priorityName) will yield a boolean value. When you use -(minus) operator on them, it is converted to numeric value(true: 1, false: 0).
So if both are neither of priority, both will yield 1 and output will be 0, which is falsey in JS.
Failure of previous expression will call current expression and would loop till last expression
var selectedName = 'fff';
var priorityName = "All";
var percentage = 50.4, count= 0;
var data=[{percentage:Math.round(percentage),count:count,name:"bbb",index:11},{percentage:Math.round(percentage),count:200,name:"aaa",index:2},{percentage:Math.round(percentage),count:400,name:"All",index:7},{percentage:Math.round(percentage),count:count,name:"fff",index:8},{percentage:Math.round(percentage),count:count,name:"ccc",index:3}];
data.sort(function(a,b){
return (a.name !== priorityName) - (b.name !== priorityName) ||
(a.name !== selectedName) - (b.name !== selectedName) ||
a.index - b.index;
})
console.log(data)
You can use a helping object holding the priority of each element. In your particular case, the highest priority has All value.
So firstly we sort the values with higher priority (All, 'fff') and when it's done, we sort the rest by the index value.
Note: Since we are sorting it by ascending order the priority values have to be negative. If we would sort it by a descending order (b - a), they would be positive.
var arr = [{percentage:Math.round("percentage"),count:"count",name:"bbb",index:11},{percentage:Math.round("percentage"),count:200,name:"aaa",index:2},{percentage:Math.round("percentage"),count:400,name:"All",index:7},{percentage:Math.round("percentage"),count:"count",name:"fff",index:8},{percentage:Math.round("percentage"),count:"count",name:"ccc",index:3}],
selectedName = 'fff',
result = arr.sort(function(a,b){
var order = {All: -2, [selectedName]: -1, default: 0};
return (order[a.name] || order.default) - (order[b.name] || order.default) || a.index - b.index;
});
console.log(result);

Javascript to validate json message

I'm working to keep validation to the incoming json message mentioned below.
"fields_group": [{
"index": 1,
"value": "test"
}, {
"index": 2,
"value": "test"
}, {
"index": 3,
"value": "test"
}, {
"index": 4,
"value": "test"
}, {
"index": 5,
"value": "test"
}]
Validations:
1) Index value should not be duplicate
2) Should allow indexes 1 to 5 only.
3) Make sure index exist for each value.
Can someone help me with Javascript that does the above in an optimal way? I tried with 2 for loops which is O(n2), but I need a faster solution.
You can use every() and add object as optional parameter to check for duplicate index values.
var obj = {"fields_group":[{"index":1,"value":"test"},{"index":2,"value":"test"},{"index":3,"value":"test"},{"index":4,"value":"test"},{"index":5,"value":"test"}]}
var result = obj.fields_group.every(function(e) {
if(!this[e.index] && e.index <= 5 && e.index > 0 && e.index) {
this[e.index] = true;
return true;
}
}, {});
console.log(result)
You can also use regular expression /^[1-5]$/ to check index values.
var obj = {"fields_group":[{"index":1,"value":"test"},{"index":2,"value":"test"},{"index":3,"value":"test"},{"index":4,"value":"test"},{"index":5,"value":"test"}]}
var result = obj.fields_group.every(function(e) {
if(!this[e.index] && /^[1-5]$/.exec(e.index)) {
this[e.index] = true;
return true;
}
}, {});
console.log(result)
Use the following approach with Array.map, Array.some and RegExp.test functions :
var obj = {"fields_group":[{"index":1,"value":"test"},{"index":2,"value":"test"},{"index":3,"value":"test"},{"index":4,"value":"test"},{"index":5,"value":"test"}]}
var isValid = function(obj){
var indexes = obj.map(function(v){ return v.index; });
return !indexes.some(function(v, k, a){ return a.lastIndexOf(v) !== k; })
&& indexes.length === indexes.map(Boolean).length
&& /^[1-5]+$/.test(indexes.join(""));
}
console.log(isValid(obj.fields_group)); // true
!indexes.some(function(v, k, a){ return a.lastIndexOf(v) !== k; }) - ensures that all indexes are unique
indexes.length === indexes.map(Boolean).length - ensures that each index value exists(not empty)
/^[1-5]+$/.test(indexes.join("") - ensures that there's should be indexes in range from 1 to 5 only
Another method:
function validate(fields_group) {
if (fields_group.length > 5) {
console.log("The array has more than 5 elements. The max is 5.");
return false;
}
var idxs = {};
for (var i = 0; i < fields_group.length; i++) {
var obj = fields_group[i];
if (obj.index == null || idxs[obj.index] || obj.index < 1 || obj.index > 5) {
console.log("An object does not have a valid index.");
return false;
} else {
idxs[obj.index] = true;
}
}
console.log("The feilds group is valid.");
return true;
}
I have measured the execution time (using performace.now() on Chrome) for the answers listed, and found this to be the fastest.

Categories

Resources