Sort array of objects based on string parameter - javascript

I have the following array of objects:
[
{
"id" : 1,
"pricePerDay" : 50,
},
{
"id" : 2,
"pricePerDay" : 70
}
]
Based on the user input from a i want to filter either ASC or DESC on the pricePerday. Obviously this would work with:
this.products.sort((a, b) => parseFloat(b.pricePerDay) - parseFloat(a.pricePerDay));
BUT i have the variable filterType which now contains the string 'pricePerDay'. How can I use this variable to search the array of objects to properties that have the same key and sort this array based on that key?

this.products.sort((a, b) => parseFloat(b[filterType]) - parseFloat(a[filterType]));
Does this answer your question?

You can change filterType to field that you want and acs to false if you want desc order
const arr = [
{
"id" : 1,
"pricePerDay" : 50,
},
{
"id" : 2,
"pricePerDay" : 70
}
]
let filterType = 'pricePerDay'
let asc = false
const res = arr.sort((a, b) => {
if (asc) {
return parseFloat(a[filterType]) - parseFloat(b[filterType])
} else {
return parseFloat(b[filterType]) - parseFloat(a[filterType])
}
});
console.log(res)

Related

How to sort double objects?

I have an array. The data in the array is in the following format.
var test = [
{
"a" : {
"order" : 100,
}
},
{
"b" : {
"order" : 10,
}
},
{
"c" : {
"order" : 1,
}
},
];
I want to sort this data according to order value. Is there any way to do this?
You can use Object.values to get the first property value and access the order property on that to compare.
let test=[{a:{order:100}},{b:{order:10}},{c:{order:1}}];
test.sort((a, b)=>Object.values(a)[0].order - Object.values(b)[0].order);
console.log(test);
For a more generalized solution, you can create a key extractor function to get the value to compare by.
let test=[{a:{order:100}},{b:{order:10}},{c:{order:1}}];
const getOrder = x => Object.values(x)[0].order;
test.sort((a, b)=>getOrder(a) - getOrder(b));
console.log(test);
You can use JS custom sort from Array.prototype.sort(), reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Then you can sort by comparing the two element's order, but you still need to determine it's key/attribute (e.g.: a or b or c)
Here, you can use Object.keys() function and take the first key in the object, reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Here's a working example:
var test = [
{
"a" : {
"order" : 100,
}
},
{
"b" : {
"order" : 10,
}
},
{
"c" : {
"order" : 1,
}
},
];
//console.log(test);
test.sort((firstEl, secondEl) => {
var key1 = Object.keys(firstEl)[0];
var key2 = Object.keys(secondEl)[0];
return firstEl[key1].order - secondEl[key2].order
} );
console.log(test);
Output:
[
{
"c": {
"order": 1
}
},
{
"b": {
"order": 10
}
},
{
"a": {
"order": 100
}
}
]

How to create a sub-associative object from a list of array keys in JavaScript?

I have an object from user input. The keys to that object are separated by commas, and I just want to separate those keys and make the keys of the object.
The key_array below is dynamic from user input, generates a different array each time, below I give you an example.
I have shown the object in my code which you can see below. you can also see the output by running that code.
var main_array = {};
var key_array = {
'user,name' : 'user name',
'user,email' : 'Email address',
'order,id' : 123456,
'order,qty' : 2,
'order,total' : 300,
'order,product,0,name' : "product1",
'order,product,0,qty' : 1,
'order,product,0,price' : 100,
'order,product,1,name' : "product2",
'order,product,1,qty' : 1,
'order,product,1,price' : 200,
};
for (keys in key_array){
var value = key_array[keys];
// What do I do here to get the output I want?
main_array['[' + keys.split(",").join('][')+ ']'] = value;
}
console.log(main_array);
Running the code above will give you the following output which is incorrect. And the output I don't want.
{
[order][id]: 123456,
[order][product][0][name]: "product1",
[order][product][0][price]: 100,
[order][product][0][qty]: 1,
[order][product][1][name]: "product2",
[order][product][1][price]: 200,
[order][product][1][qty]: 1,
[order][qty]: 2,
[order][total]: 300,
[user][email]: "Email address",
[user][name]: "user name"
}
I want an output like JSON below, so please tell me how to do it.
{
"user":{
"email" : "Email address",
"name" : "user name"
},
"order":{
"id" : 123456,
"qty" : 2,
"total" : 300,
"product":[
{
"name" : "product1",
"price" : 100,
"qty" : 1
},{
"name" : "product2",
"price" : 200,
"qty" : 1
}
]
}
}
Note: Please do not use eval, as using eval in this way is terribly unreliable, bad work and unsafe. Because I get all my data from user input, the likelihood of abuse can increase.
Use Object.entries to go over key and values of object.
Split the key by , separator and then build the object.
While building object, make sure to merge the keys and values using mergeTo method.
Then convert the objects which has the numerical keys then convert to object using convertObjsToArray method.
var key_array = {
"user,name": "user name",
"user,email": "Email address",
"order,id": 123456,
"order,qty": 2,
"order,total": 300,
"order,product,0,name": "product1",
"order,product,0,qty": 1,
"order,product,0,price": 100,
"order,product,1,name": "product2",
"order,product,1,qty": 1,
"order,product,1,price": 200
};
const mergeTo = (target, obj) => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object" && !Array.isArray(value)) {
if (!target[key]) {
target[key] = {};
}
mergeTo(target[key], obj[key]);
} else {
target[key] = value;
}
});
};
const convertObjsToArray = obj => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object") {
if (Object.keys(value).every(num => Number.isInteger(Number(num)))) {
obj[key] = Object.values(value);
} else {
convertObjsToArray(obj[key]);
}
}
});
};
const res = {};
Object.entries(key_array).map(([key, value]) => {
const keys = key.split(",");
let curr = { [keys.pop()]: value };
while (keys.length > 0) {
curr = { [keys.pop()]: curr };
}
mergeTo(res, curr);
});
convertObjsToArray(res);
console.log(res);
You can create the objects and keys required from the string dynamically, take each key and split it to an array using split(','). Using each item in the array create the structure required. Assuming if a key is a number, then it's parent must be an array.
Object.keys(key_array).forEach(key => {
const path = key.split(',');
let current = main_array;
for (let i = 0; i < path.length - 1; i++) {
if (!current[path[i]]) {
current[path[i]] = path[i + 1] && !isNaN(path[i + 1]) ? [] : {};
}
current = current[path[i]];
}
current[path.pop()] = key_array[key];
});
console.log(main_array); // Desired result

Form Array of objects using an array and an object

I have an object and an array of following kind
var sourceObject = { "item1" : 15 , "item2" : 20 " }
var feature = ["field1", "field2" ]
I am trying to convert the above object into an array of objects.
Number of items in the object as well as the array will be same
Resultant array of objects should like this:
var result = [ { "name" : "field1" , "value" : 15 } , { "name" : "field2" , "value": 20 }]
The ultimate goal is to read it from the sourceObject to get the each value and then pick each value from the "feature" array toform an object
Approach I have tried so far:
let result = [];
for (let value of Object.values(sourceObject)) {
let row = { "field" : "XYZ" , "value": value };
tableData.push(row);
}
Loop over the keys of sourceObject and then use Array.map()
var sourceObject = {
"item1": 15,
"item2": 20
}
var feature = ["field1", "field2"]
var result = Object.keys(sourceObject).map((key, index) => {
return {
name: feature[index],
value: sourceObject[key]
}
});
console.log(result);
Your object doesn't always guarantee order, so using .values(), .keys() etc... won't necessarily always guarantee your result. Instead, you can get the number from your fieldN string using a regular expression. Here N represents the itemN you want to retrieve from your object. Using this, you can .map() each fieldN to an object from your sourceObject.
See example below:
const sourceObject = { "item1" : 15 , "item2" : 20 };
const feature = ["field1", "field2" ];
const res = feature.map((name, i) => {
const [n] = name.match(/\d+$/g);
const value = sourceObject[`item${n}`];
return {name, value};
});
console.log(res);

How to sort by name descending in lodash?

I have an array of objects that are sorted in descending order by date:
_.sortBy
(persons.entities.alerts,
dateObj => new Date(dateObj.createdDateTime)
).reverse()
This is the array:
let persons={
"entities": {
"applicants": [
{
"lastName": "Agamemnon",
"isPrimaryApplicant": true,
"id": "16671520038"
},
{
"lastName": "Purdy",
"isPrimaryApplicant": false,
"id": "16671520039"
},
{
"lastName": "Brekky",
"isPrimaryApplicant": true,
"id": "16671520040"
},
{
"lastName": "Tabouli",
"isPrimaryApplicant": true,
"id": "16671520041"
}
],
"alerts": [
{
"createdDateTime": "2018-06-14T00:00:00.000Z",
"applicants": ["16671520038", "16671520039"],
"id": "05025fea-ec37-4767-a868-a646597365d0"
},
{
"createdDateTime": "2018-06-14T00:00:00.000Z",
"applicants": ["16671520040"],
"id": "19d0da63-dfd0-4c00-a13a-cc822fc83869"
},
{
"createdDateTime": "2018-06-14T00:00:00.000Z",
"applicants": ["16671520041"],
"id": "c5385595-2104-409d-a676-c1b57346f63e"
}
]
}
}
The sort returns the correct order by date desc. In this sample the dates are the same. Only in this case i want to sort by (applicants) lastName where isPrimaryApplicant=true? Link to codepen
You want lodash's orderBy, which allows sort directions.
You can attach asc or desc to each sort property you use.
This should get you the ordering you're looking for:
_.orderBy(persons.entities.applicants, ['lastName'], ['desc'])
Loadash sortBy doesn't provide option for comparator function(though there are other ways to achieve it)
You can use array sort method to achieve this:
persons.entities.alerts.sort(function(a1, a2) {
if(a1.createdDateTime === a2.createdDateTime) {
let applicant1 = persons.entities.applicants.find(a => a.id === a1.applicants[0]);
let applicant2 = persons.entities.applicants.find(a => a.id === a2.applicants[0]);
if (!applicant1.isPrimaryApplicant || applicant1.lastName < applicant2.lastName) {
return -1;
}
return 1;
} else {
let d1 = new Date(a1.createdDateTime);
let d2 = new Date(a2.createdDateTime);
return d2 - d1;
}
})
Would have loved to use lodash for this but the documentation does not reflect reality. The second argument to _.sortBy is an array in documentation but doesn't work if I pass an array of functions.
You can add names to your alerts and while your at it add a sortDate to use for sorting:
const persons={"entities":{"applicants":[{"lastName":"Agamemnon","isPrimaryApplicant":true,"id":"16671520038"},{"lastName":"Purdy","isPrimaryApplicant":false,"id":"16671520039"},{"lastName":"Brekky","isPrimaryApplicant":true,"id":"16671520040"},{"lastName":"Tabouli","isPrimaryApplicant":true,"id":"16671520041"}],"alerts":[{"createdDateTime":"2018-06-14T00:00:00.000Z","applicants":["16671520038","16671520039"],"id":"05025fea-ec37-4767-a868-a646597365d0"},{"createdDateTime":"2018-06-14T00:00:00.000Z","applicants":["16671520041"],"id":"19d0da63-dfd0-4c00-a13a-cc822fc83869"},{"createdDateTime":"2019-06-14T00:00:00.000Z","applicants":["16671520040"],"id":"c5385595-2104-409d-a676-c1b57346f63e"}]}}
const applicantsById = persons.entities.applicants.reduce(
(result, applicant) => result.set(applicant.id, applicant),
new Map(),
);
const alertsWithName = persons.entities.alerts.map((alert) => ({
...alert,
sortDate:new Date(alert.createdDateTime).getTime(),
name: (alert.applicants
.map((id) => applicantsById.get(id))
.filter((x) => x) //remove empty
.find((applicant)=>applicant.isPrimaryApplicant)||{lastName:''}).lastName
}));
//according to not correct lodash documentation here:
//https://lodash.com/docs/4.17.11#sortBy
//we should be able to do this:
// console.log(
// _.sortBy(alertsWithName, [
// (alert) => new Date(alert.createdDateTime),
// (alert) => alert.name,
// ])
// )
//however that's not going to work so can try array sort method
console.log(
alertsWithName.sort(
(a,b)=>b.sortDate-a.sortDate || a.name.localeCompare(b.name)
)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.core.js"></script>

Does JQuery have a sort_by function similar to Ruby's

So given a list of items like so:
item_1 = {id:1, categories: {"category_A" => 1, "category_B" => {"sub_category_A" => 3, "sub_category_B" => 1}}}
item_2 = {id:2, categories: {"category_B" => {"sub_category_A" => 1, "sub_category_B" => 2}}}
Where the numeric value is that items order in a given sub or main category. Now, given a sub or main category, I want to sort the items by the order number. In Ruby I'd write...
# Given category_B and sub_category_A
items.sort_by { |i| i.categories["category_B"]["sub_category_A"] }
# which would return...
[item_2, item_1]
Also want to add, the key is if an item does NOT have the relevant passed category_B and sub_category_A, it should be excluded entirely from output.
You don't need jQuery; JavaScript arrays have a filter() function you can use to limit yourself to valid items and a sort() function that can take a comparing function as its argument:
var item_1 = {
id:1,
categories: {
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
};
var item_2 = {
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
};
var item_3 = {
id: 3,
categories : {
"category_A" : 2
}
};
[item_1,item_2,item_3].filter(function(entry) {
return entry.categories.category_B;}).sort(function(left, right) {
return left.categories["category_B"]["sub_category_A"] -
right.categories["category_B"]["sub_category_A"]
});
// or in the more readable ES6 style
[item_1,item_2,item_3]
.filter((entry) => entry.categories.category_B)
.sort((left, right) => left.categories["category_B"]["sub_category_A"] - right.categories["category_B"]["sub_category_A"]
);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
You can use Array#Sort in JavaScript.
a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"]
Is the comparison of 2 elements in Array.
UPDATE: to excluded from the output. You can use Array#filter it before sorting
Let's see my example
var arr = [{
id:1,
categories:
{
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
},{
id:3,
categories: {
"category_C" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
},{
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
}];
var result = arr.filter(a => a.categories["category_B"])
.sort((a, b) => {
return a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"];
})
console.log(result);
Neither ECMAScript nor jQuery have sortBy, but LoDash does and so does Underscore.
It's also not hard to supply your own:
Array.prototype.sortBy = function sortBy(keyFn) {
const compare = (a, b) => a > b ? 1 : (a < b ? -1 : 0);
return this.
map(el => [keyFn(el), el]).
sort(([a, ], [b, ]) => compare(a, b)).
map(([, el]) => el);
};
const item1 = { id: 1, categories: { category_A: 1, category_B: { sub_category_A: 3, sub_category_B: 1 }}};
const item2 = { id: 2, categories: { category_B: { sub_category_A: 1, sub_category_B: 2 }}};
const sorted = [item1, item2].sortBy(el => el.categories.category_B.sub_category_A);
console.log(sorted);

Categories