Array Sorting in an object in javascript - javascript

I have to sort the arrays of the "Key" in ascending order and here's what I am doing .
Surprsingly the first array is getting sorted in descending order and the next two in ascending order . Whats the possible reason for this?
var MainObject4 = [{ "mainarray": [{ "Key": [9,768,78] },
{ "Key": [9,4,1] },{ "Key": [49,89,54] }]
}];
var first = MainObject4[0];
Object.keys(MainObject4[0]).forEach(function (k) {
first[k].forEach(function (j) {
Object.keys(j).forEach(function (g) {
j[g].sort();
},this);
},this);
},this);
alert(JSON.stringify(MainObject4, 0, 4));
Expected output:
[9,78,768]
[1,4,9]
[49,54,89]
Output I am getting now:
[768,78,9]
[1,4,9]
[49,54,89]

See the doc of Array.sort()
The default sort order is according to string Unicode code points.
If you want to compare numbers, you need to provide the comparison function, as said in the doc :
To compare numbers instead of strings, the compare function can simply subtract b from a.

You need to add a function to compare sort parameters:
var MainObject4 = [{
"mainarray": [{
"Key": [9, 768, 78]
}, {
"Key": [9, 4, 1]
}, {
"Key": [49, 89, 54]
}]
}];
var first = MainObject4[0];
Object.keys(MainObject4[0]).forEach(function(k) {
first[k].forEach(function(j) {
Object.keys(j).forEach(function(g) {
j[g].sort(function(a, b) {
return a - b;
});
}, this);
}, this);
}, this);
alert(JSON.stringify(MainObject4, 0, 4));

var MainObject4 = [{ "mainarray": [
{ "Key": [9,768,78] },
{ "Key": [9,4,1] },
{ "Key": [49,89,54] }
]
}];
MainObject4[0].mainarray.forEach(function (j) {
Object.keys(j).forEach(function (g) {
j[g].sort(function (a, b) {
return a - b;
});
},this);
},this);
alert(JSON.stringify(MainObject4, 0, 4));

It's happening because of JavaScript's sort method takes by default unicode to compare the elements. For sorting numbers, you have to explicitly write a call back function to compare elements. For ex
var input = [1,20,2,12];
console.log(input.sort());// prints 1,12,2,20
console.log(input.sort(function(a,b){
return a-b;
}));// prints 1,2,12,20
So, You just need to add the compare function to your sort. That's all

Related

Returning data after sorting through multiple arrays

I have nested arrays within an object, and I need to extract these values, and sort them into alphabetical order which will then be displayed in a table.
I am using the localeCompare method but it is returning:
Cannot read property 'localeCompare' of null
To be honest, I am not sure I am approaching this correctly. Using sort you can compare a to b to sort them into alphabetical order. I am getting confused how to compare the values from the arrays within a and b. I am using the first sort on tableData to access the array, and the using a second sort to compare the values that I pushed to array clientRefArr
if(params.sorting().clientRef) {
var clientRefArr = [];
tableData.sort(function(a, b){
a.renewalUIs.forEach(function(data, i){
clientRefArr.push(data.patentUI.clientRef);
})
return clientRefArr.sort(function(a, b){
console.log(a.localeCompare(b))
// return a.localeCompare(b)
})
})
orderedData = tableData;
}
return orderedData;
Question
Why is the error Cannot read property 'localeCompare' of null being returned?Am I approaching this issue completely wrong?
//JSON
[
0: {
transRef: "IX1000013"
renewalProgress: 30
renewalUIs: [
0: {
patentUI: {
business: null
clientRef: P0101011 // this is the required value
}
renewalAttemptsMade: 1
renewalDueDate: 1514764740000
}
]
},
1: {
transRef: "IX100333"
renewalProgress: 55
renewalUIs: [
0: {
patentUI: {
business: null
clientRef: P0101011 // this is the required value
}
renewalAttemptsMade: 1
renewalDueDate: 1514764740000
},
1: {
patentUI: {
business: null
clientRef: P5551011 // this is the required value
}
renewalAttemptsMade: 3
renewalDueDate: 174834740000
}
]
}
]
You could take a default value for both parts
(a || '').localeCompare(b || '')
for sorting the null values or even all falsy values to top.
An attempt with the given data (I changed some value, to get some sorting).
It now sorts the inner arrays renewalUIs first and then it takes the first element for sorting the outer array.
var array = [{ transRef: "IX1000013", renewalProgress: 30, renewalUIs: [{ patentUI: { business: null, clientRef: "P9101011" }, renewalAttemptsMade: 1, renewalDueDate: 1514764740000 }] }, { transRef: "IX100333", renewalProgress: 55, renewalUIs: [{ patentUI: { business: null, clientRef: "P7101011" }, renewalAttemptsMade: 1, renewalDueDate: 1514764740000 }, { patentUI: { business: null, clientRef: "P5551011" }, renewalAttemptsMade: 3, renewalDueDate: 174834740000 }] }];
array.forEach(function (o) {
o.renewalUIs.sort(function (a, b) {
return a.patentUI.clientRef.localeCompare(b.patentUI.clientRef);
});
});
array.sort(function (a, b) {
return a.renewalUIs[0].patentUI.clientRef.localeCompare(b.renewalUIs[0].patentUI.clientRef);
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
IF my interpretation is correct, it'd be way easier to first put everything in an single array and then sort it as such just use loops for the first task and a normal sort for the second, nested sorts are really just a bad idea.
// let's assume your data is in unorderedData
var clientUIs = [];
for (var i = 0; i < unorderedData.length; i++){
for (var j = 0 ; j < unorderedData[i]["renewalUIs"].length; j++){
// add each UI to your list individually.
clientUIs.push(unorderedData[i]["renewalUIs"][j]["patentUI"]);
}
}
clientUIs.sort() //just sort everything
tableData = clientUIs;
If "renewalUIs" or "patentUIs" arent constant you can iterate over the keys of the dictionary.

Sorting array of numbers

I have some certain things which will be pushed into array like that (down below) and I would like to sort the item(s) by the item price. I tried using a sort but it doesn't work for me. After that I would like to loop that array and retrieve the item name and a item price.
Pushing item into array: array.push(itemName,itemPrice)
Trying to use sort function:
array.sort(function(a,b){
return b[1] - a[1]
});
EDIT: Agree with Nina's comment. changing compare function
Looks like you need an array of objects and define your own comparison function for sorting.
So you need to add element to array like this:
array.push({itemName:name,itemPrice:price})
Define custom comparison function
function compare(a,b) {
return a.itemPrice-b.itemPrice;
}
Use custom function
array.sort(compare)
I think you want to push the tuple (itemName, itemPrice) to array which can be done either way:
array.push([itemName, itemPrice])
or
array.push({ itemName: itemName, itemPrice: itemPrice })
// and instead of accessing a[1] or b[1], access a.itemPrice and b.itemPrice respectively.
array.push(x, y) simply appends two different elements x and y to the array
I think what you want to do is push objects not just values!
array.push( {
name: itemName,
price: itemPrice
} );
Which you can then sort using:
array.sort(function(a, b){
return b.price - a.price;
});
// Sort BY keys( and values).
int[] keys = { 4, 7, 2, 0 };
int[] values = { 1, 2, 3, 4 };
Array.Sort(keys, values);
foreach (int key in keys)
{
Console.Write(key);
Console.Write(' ');
}
Console.WriteLine();
foreach (int value in values)
{
Console.Write(value);
Console.Write(' ');
}
Console.WriteLine();
I hope this will help you ;)
here is a sample code, have fun !
var arr = [
{
"name": "Item1",
"price": 100,
},
{
"name": "Item2",
"price": 500,
},
{
"name": "Item3",
"price": 250,
},
{
"name": "Item4",
"price": 1000,
}
]
arr = arr.sort(function compare(a, b) {
if (a.price > b.price)
return 1;
if (a.price < b.price)
return -1;
return 0;
});
console.log(arr);

sorting an orderedMap by a specific key in Immutable.js

Please check this fiddle example
How can I sort an orderedMap object by the keys sequence in a descending order? I know there is a sortBy method but the document doesn't give a clearer example.
Here's my original object:
var props = {
"item": {
"A": {
"sequence": 5
},
"B": {
"sequence": null
},
"C":{
"sequence": 2
}
}
}
I'd like the result to be like:
var props = {
"item": {
"A": {
"sequence": 5
},
"C":{
"sequence": 2
},
"B": {
"sequence": null
}
}
}
Example Code:
var data = Immutable.fromJS(props,(key, value)=>{
var isIndexed = Immutable.Iterable.isIndexed(value);
return isIndexed ? value.toList() :value.toOrderedMap();
});
var sorted = data.get('item').sortBy((item) => item.get('sequence'));
console.log(sorted.toJS())
The function you are providing to sortBy() is comparator and it needs to return a Number. By adding a minus in front of the item.get('sequence') or following the result with reverse() you will reverse the sort, getting you the output that you want.
Here's an example:
var sorted = data.get('item').sortBy((item) => -item.get('sequence'));
// or var sorted = data.get('item').sortBy((item) => item.get('sequence')).reverse();
// you can now use .map() to go through your OrderedMap which has been
// sortbed by the sequence property and would look like your desired output
Note that you are working with an OrderedMap and that simply calling sorted.toJS() would return a regular JS object where the keys are, obviously, not sorted.
// If you need to iterate through the map and return some
// plain JS you can do something like:
sorted.map(x => x.get('sequence')).toJS()
// => Object {A: 5, C: 2, B: null}

Merge Arrays Combining Matching Objects in Angular Javascript

I have 2 array objects in Angular JS that I wish to merge (overlap/combine) the matching ones.
For example, the Array 1 is like this:
[
{"id":1,"name":"Adam"},
{"id":2,"name":"Smith"},
{"id":3,"name":"Eve"},
{"id":4,"name":"Gary"},
]
Array 2 is like this:
[
{"id":1,"name":"Adam", "checked":true},
{"id":3,"name":"Eve", "checked":true},
]
I want the resulting array after merging to become this:
[
{"id":1,"name":"Adam", "checked":true},
{"id":2,"name":"Smith"},
{"id":3,"name":"Eve", "checked":true},
{"id":4,"name":"Gary"},
]
Is that possible? I have tried angular's array_merge and array_extend like this:
angular.merge([], $scope.array1, $scope.array2);
angular.extend([], $scope.array1, $scope.array2);
But the above method overlap the first 2 objects in array and doesn't merge them based on matching data. Is having a foreach loop the only solution for this?
Can someone guide me here please?
Not sure if this find of merge is supported by AngularJS. I've made a snippet which does exactly the same:
function merge(array1, array2) {
var ids = [];
var merge_obj = [];
array1.map(function(ele) {
if (!(ids.indexOf(ele.id) > -1)) {
ids.push(ele.id);
merge_obj.push(ele);
}
});
array2.map(function(ele) {
var index = ids.indexOf(ele.id);
if (!( index > -1)) {
ids.push(ele.id);
merge_obj.push(ele);
}else{
merge_obj[index] = ele;
}
});
console.log(merge_obj);
}
var array1 = [{
"id": 1,
"name": "Adam"
}, {
"id": 2,
"name": "Smith"
}, {
"id": 3,
"name": "Eve"
}, {
"id": 4,
"name": "Gary"
}, ]
var array2 = [{
"id": 1,
"name": "Adam",
"checked": true
}, {
"id": 3,
"name": "Eve",
"checked": true
}, ];
merge(array1, array2);
Genuinely, extend in Angular works with object instead of array. But we can do small trick in your case. Here is another solution.
// a1, a2 is your arrays
// This is to convert array to object with key is id and value is the array item itself
var a1_ = a1.reduce(function(obj, value) {
obj[value.id] = value;
return obj;
}, {});
var a2_ = a2.reduce(function(obj, value) {
obj[value.id] = value;
return obj;
}, {});
// Then use extend with those two converted objects
var result = angular.extend([], a1_, a2_).splice(1)
Notes:
For compatibility, reduce may not work.
The after array will replace the previous one. This is because of implementation of extend in Angular.

Summing arrays in JavaScript using d3.nest() Part II

I recently posted this question about summing arrays in JavaScript using d3.nest()
I got a good solution (two in fact), but it turns out both have an issue when adapted to append additional information:
data = [
{
continent: 'africa',
country: 'gabon',
values: [1, 2]
}, {
continent: 'africa',
country: 'chad',
values: [3, 4]
}, {
continent: 'asia',
country: 'china',
values: [1, 2]
}
];
sum_by = 'continent';
rollupAgg = function(data, sum_by) {
return d3.nest().key(function(d) {
return d[sum_by];
}).rollup(function(group) {
return group.reduce(function(prev, cur, index, arr) {
return {
values: prev.values.map(function(d, i) {
return d + cur.values[i];
}),
sum_by: sum_by // additional information
};
});
}).entries(data);
};
reduce() doesn't run if there is only one row in that group, so this returns the following:
[
{
"key": "africa",
"values": {
"values": [4, 6],
"sum_by": "continent"
}
}, {
"key": "asia",
"values": {
"continent": "asia",
"country": "china", // country information shouldn't have been retained
"values": [1, 2]
} // sum_by information should have been added
}
];
Can you see a way to modify this function so that it returns the desired result?
Hasn't occurred to me till now that a single element array will not execute the function; but it makes sense, because you're using with a single param:
[].reduce(function(prev, curr, i, arr) {});// <-- function is the only param
When used with a single param, the behavior is that the first time the function is executed, i equals 1 (not 0), and prev and curr are the first and second elements of the array. Since you only have one element, there's no way to call it in this form.
If you use reduce with a 2nd param:
[].reduce(function(prev, curr, i, arr) {}, { foo:'bar' });// <-- 2nd param {foo:bar}
it does actually call the function, with the first call passing i equal to 0 and prev equaling { foo:'bar' }.
So I guess you have 2 options:
Either modify it to pass a 2nd param, which in your case would need to be { values:[0,0] } (and that hardcodes the fact that values is always 2 elements, which will cause an issue if it's longer).
Check if group.length == 1 and if so, return group, instead of calling reduce.

Categories