how to sort array of objects by condition javascript - javascript

I have array of objects with properties.
I would like to sort by status, that is
15, 17 then 16 at last in javascript
For a array of objects , status having value 16
should be placed at last and rest should sort by ascending as the expected output.
How to do in javascript
var result = arrobj.filter(e=>e.details.status !== 16).sort(a, b) => a.status - b.status;
var arrobj = [
{
"id":1,
"name": 'xyz',
"details": {
"job": 'fulltime',
"status": 15
}
},
{
"id":2,
"name": 'abc',
"details": {
"job": 'partime',
"status": 16
}
},
{
"id":3,
"name": 'zen',
"details": {
"job": 'fulltime',
"status": 17
}
},
{
"id":5,
"name": 'abu',
"details": {
"job": 'fulltime',
"status": 16
}
},
{
"id":7,
"name": 'john',
"details": {
"job": 'parttime',
"status": 15
}
},
{
"id":10,
"name": 'jocob',
"details": {
"job": 'fulltime',
"status": 17
}
}
]
Expected Output
[
{
"id":1,
"name": 'xyz',
"details": {
"job": 'fulltime',
"status": 15
}
},
{
"id":7,
"name": 'john',
"details": {
"job": 'parttime',
"status": 15
}
},
{
"id":3,
"name": 'zen',
"details": {
"job": 'fulltime',
"status": 17
}
},
{
"id":10,
"name": 'jocob',
"details": {
"job": 'fulltime',
"status": 17
}
},
{
"id":2,
"name": 'abc',
"details": {
"job": 'partime',
"status": 16
}
},
{
"id":5,
"name": 'abu',
"details": {
"job": 'fulltime',
"status": 16
}
}
]

const results = [...arrobj.filter(ob => ob.details.status !== 16).sort((a,b) => a.details.status - b.details.status), ...arrobj.filter(ob => ob.details.status === 16)]
You mean this?

We can customize sort rules using the compareFn in Array.prototype.sort(compareFn).
Example:
var result = arrobj
.sort((obja, objb) => {
let a = obja.details.status
let b = objb.details.status
if (a == 16) a = Number.MAX_SAFE_INTEGER
if (b == 16) b = Number.MAX_SAFE_INTEGER
return a - b
})

for es5
arrobj.sort((a, b) => {
if (a.details.status === 16) {
return 1;
} else if (b.details.status === 16) {
return -1
} else {
return a.details.status - b.details.status
}
})

From EcmaScript 2019 Array.sort is stable. This mean that you can split that complex sorting into 2 - first by status, then placing all items with status 16 in the back. Not efficient solution
arrobj.sort((first,second) => first.details.status - second.details.status)
.sort((first, second) => (first.details.status === 16) - (second.details.status === 16));

The below will also work for your use case. You were really close though
arrobj.filter(e=>e.details.status !== 16).sort((a, b) => {return a.details.status - b.details.status}).concat(arrobj.filter(e=>e.details.status == 16));

Your sort function is not correct. Try this one. I also add the ability to change the sort order.
// Sort by status
function sortByStatus(array, asc = true) {
let newArray = [...array];
// filter and sort
if (asc) {
newArray = newArray.filter(e => e.details.status !== 16).sort((a, b) => a.details.status > b.details.status && 1 || -1);
} else {
newArray = newArray.filter(e => e.details.status !== 16).sort((a, b) => a.details.status < b.details.status && 1 || -1);
}
return newArray;
}
// merge result
const result = [...sortByStatus(arrobj), arrobj.filter(e => e.details.status === 16)];
var arrobj = [{
"id": 1,
"name": 'xyz',
"details": {
"job": 'fulltime',
"status": 15
}
},
{
"id": 2,
"name": 'abc',
"details": {
"job": 'partime',
"status": 16
}
},
{
"id": 3,
"name": 'zen',
"details": {
"job": 'fulltime',
"status": 17
}
},
{
"id": 5,
"name": 'abu',
"details": {
"job": 'fulltime',
"status": 16
}
},
{
"id": 7,
"name": 'john',
"details": {
"job": 'parttime',
"status": 15
}
},
{
"id": 10,
"name": 'jocob',
"details": {
"job": 'fulltime',
"status": 17
}
}
];
// Sort by status
function sortByStatus(array, asc = true) {
let newArray = [...array];
// filter and sort
if (asc) {
newArray = newArray.filter(e => e.details.status !== 16).sort((a, b) => a.details.status > b.details.status && 1 || -1);
} else {
newArray = newArray.filter(e => e.details.status !== 16).sort((a, b) => a.details.status < b.details.status && 1 || -1);
}
return newArray;
}
// merge result
const result = [...sortByStatus(arrobj), arrobj.filter(e => e.details.status === 16)];
console.log(result);

You had to add .status in the sort function sort()
To place the object with status 16 at the end, I made an seperate array with status 16 and added on the end of the array with everything else.
var result = arrobj.filter(e=>e.details.status !== 16).sort( (a, b) => a.details.status - b.details.status);
result = result.concat(arrobj.filter(e=>e.details.status == 16));
console.log(result)

Related

Get object value based on conditions within nested array objects

I have an array of objects called orders:
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}
]
}
];
I need to get the quantity when both the order_type (Wholesale) and items.detail.ID (13363) match.
I have so far tried the following:
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if(item.detail.ID == 13363) {
return item.quantity;
}
}, 0);
Where result correctly returns 4
My issue, and I'm sure I am missing something very simple is that when I have multiple items in my orders array, it fails.
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
},
{
"id": 56,
"department": "Womens",
"type": "Skirt",
"quantity": 12,
"detail": {
"ID": 76884,
"On Sale": 0,
}
},
{
"id": 89,
"department": "Mens",
"type": "Shirts",
"quantity": 20,
"detail": {
"ID": 98223,
"On Sale": 1,
}
}
]
}
];
The same
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if(item.detail.ID == 13363) {
return item.quantity;
}
}, 0);
returns undefined
Thank you
The find helper just returns the first match, so you need to use another helper like filter, like this:
const ID = 13363;
const result = orders
.filter((order) => order.order_type === 'Wholesale')
.reduce((acc, curr) => {
const items = curr.items.filter((item) => item.detail.ID === ID);
console.log(items);
// You can sum the matching items and then push them into the acc array
const quantity = items.reduce((sum, item) => (sum += item.quantity), 0);
acc.push(quantity);
return acc;
}, []);
This will return an array of matching quantities.
Not sure about the use case but here you go
const result = orders.find(item => item.order_type == "Wholesale").items
.reduce((total, item) => {
if (item.detail.ID == 13363) {
total += item.quantity;
}
return total
}, 0);
You can even create a function to make the search dynamic.
const orders = [
{
"order_id": 47445,
"order_type": "Wholesale",
"items": [
{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
},
{
"id": 56,
"department": "Womens",
"type": "Skirt",
"quantity": 12,
"detail": {
"ID": 76884,
"On Sale": 0,
}
},
{
"id": 89,
"department": "Mens",
"type": "Shirts",
"quantity": 20,
"detail": {
"ID": 98223,
"On Sale": 1,
}
}
]
}
];
findMyItem=( ID )=>{
var result = null ;
const result2 = orders.find(item => item.order_type == "Wholesale").items
.map(( item) => {
if(item.detail.ID == ID ) {
result = item.quantity;
}
}, 0);
return result ;
}
console.log( "result" ,findMyItem( 13363 ) )
console.log( "result" ,findMyItem( 98223) )
console.log( "result" ,findMyItem( 76884) )
You could use Array.find() on the orders array to find the correct order, searching for the first order that matches both the order_type and has an item matching the desired itemId (using Array.some()).
If this order exists, we can then find the corresponding item quantity using .find() again,
const orders = [ { "order_id": 47445, "order_type": "Wholesale", "items": [ { "id": 9, "department": "Womens", "type": "Dress", "quantity": 4, "detail": { "ID": 13363, "On Sale": 1, } }, { "id": 56, "department": "Womens", "type": "Skirt", "quantity": 12, "detail": { "ID": 76884, "On Sale": 0, } }, { "id": 89, "department": "Mens", "type": "Shirts", "quantity": 20, "detail": { "ID": 98223, "On Sale": 1, } } ] } ]
function findItemQuantity(orders, orderType, itemId) {
// Find the first order with the right order_type and containing the right item id
const order = orders.find(order => order.order_type = orderType && order.items.some(item => item.detail.ID === itemId));
if (!order) {
return null;
}
const item = order.items.find(item => item.detail.ID === itemId);
if (!item) {
return null;
}
return item.quantity;
}
console.log("Quantity found:", findItemQuantity(orders, 'Wholesale', 13363))
console.log("Quantity found:", findItemQuantity(orders, 'Wholesale', 76884))
const result = orders
.filter(order => order.order_type == "Wholesale")
.map(order => order.items.find(item => item.detail.ID == 13363))
.filter(item => item)
.reduce((total, { quantity }) => quantity + total, 0);
const orders = [{
"order_id": 47445,
"order_type": "Wholesale",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
},
{
"order_id": 47445,
"order_type": "Whole",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
}
]
const result = orders.reduce(v => {
return v.items.map(a => {
if (v.order_type == 'Wholesale' && a.detail.ID == 13363) {
return v
}
})
})
console.log(result)
const orders = [{
"order_id": 47445,
"order_type": "Wholesale",
"items": [{
"id": 9,
"department": "Womens",
"type": "Dress",
"quantity": 4,
"detail": {
"ID": 13363,
"On Sale": 1,
}
}]
}];
var result = null;
const result2 = orders.find(item => item.order_type == "Wholesale").items
.map((item) => {
if (item.detail.ID == 98223) {
result = item.quantity;
}
}, 0);
console.log("result", result)

Find the latter date in an object

I need iterate specific objects in an object, and find the object with latter Date.
Here is example of my object:
var o = {
"data": [
{
"id": 2,
"category": "test1",
"parents": [
{
"id": 31,
"children": [
{
"firstName": "Steve",
"lastName": "Martin",
"created": "2018-04-06T22:00:00.000Z"
},
{
"firstName": "Steve2",
"lastName": "Martin2",
"created": "2016-02-10T23:00:00.000Z"
}
]
},
{
"id": 31,
"children": [
{
"firstName": "Julia",
"lastName": "Robbery",
"created": "2015-01-06T23:00:00.000Z"
},
{
"firstName": "Nikol",
"lastName": "Surachenko",
"created": "2017-04-06T22:00:00.000Z"
},
{
"firstName": "Nikol",
"lastName": "Surachenko",
"created": "2011-06-05T22:00:00.000Z"
}
]
}
]
}
]
}
I tried this:
var latter = null;
for (var i = 0; i < o.data[0].parents.length; i++) {
for (var j = 0; j < o.data[0].parents[i].children.length; j++) {
if (latter == null || moment(latter) < moment(o.data[0].parents[i].children[j].created))
latter=o.data[0].parents[i].children[j].created;
}
}
Can you tell me if exist some prettier way? For example with lambda, etc.?
Thanks in advice.
"pretty" is subjective, but in my opinion with lodash you could write it in a bit cleaner way:
mostRecent = _.max(
_.flatMap(
_.get(o, 'data.0.parents'),
'children'),
'created')
If lodash is not an option, you can roll out your own ad-hoc microframework:
let get = p => o => o[p];
let flatMap = (a, f) => [].concat(...a.map(f));
let max = (a, f) => a.map(x => [x, f(x)]).reduce((m, p) => m[1] > p[1] ? m : p)[0];
mostRecent = max(
flatMap(o.data[0].parents, get('children')),
get('created')
)
Using for-loops and compare dates.
This approach downstreams into the whole object to get the right object.
var o = { "data": [{ "id": 2, "category": "test1", "parents": [{ "id": 31, "children": [{ "firstName": "Steve", "lastName": "Martin", "created": "2018-04-06T22:00:00.000Z" }, { "firstName": "Steve2", "lastName": "Martin2", "created": "2016-02-10T23:00:00.000Z" } ] }, { "id": 31, "children": [{ "firstName": "Julia", "lastName": "Robbery", "created": "2015-01-06T23:00:00.000Z" }, { "firstName": "Nikol", "lastName": "Surachenko", "created": "2017-04-06T22:00:00.000Z" }, { "firstName": "Nikol", "lastName": "Surachenko", "created": "2011-06-05T22:00:00.000Z" } ] } ] }] }
var result = {};
for (var obj of o.data) {
for (var p of obj.parents) {
for (var c of p.children) {
result = !result.created || Date.parse(c.created) > Date.parse(result.created) ? c : result;
}
}
}
console.log(result);
You might do it a bit more functional:
const result = o.data[0].parents.reduce((res, {children}) => res.concat(children), [])
.reduce((old, curr) => Date(old.created) > Date(curr.created) ? old : curr);

Issue with JavaScript recursive function

I have an object with nested objects. I need to get all the keys and values from all the sub objects into one array.
So I'm trying to do it with a recursive function, but I guess I'm doing something wrong...
The object :
var jsonobj = {
"gender": "male",
"country": "us",
"phone": "06 12 34 56 78",
"enterprise": {
"parameters": {
"company": "foo",
"companyID": "12345678912345",
"address": "adress principale",
}
},
"contacts": [],
"requirements": []
}
Here is the function :
function check(arr){
var val = '';
$.each(arr, function(k, v) {
if (typeof v == "object" && v.length !== 0) {
val = check(v);
}
});
return val;
}
And this is the function using it :
function rec_res(obj_res) {
var foo=[];
$.each(jsonobj, function(k, v) {
if (typeof v == "object" && v.length !== 0) {
g = check(jsonobj); // calling the function
foo.push(g);
} else {
foo.push(v);
}
});
console.log(foo);
};
Expected output:
[foo:{
"gender": "male",
"country": "us",
"phone": "06 12 34 56 78",
"company": "foo",
"companyID": "12345678912345",
"address": "adress principale",
}]
Fiddle
You can create recursive function with Object.keys() and reduce() methods.
var jsonobj = {
"gender": "male",
"country": "us",
"phone": "06 12 34 56 78",
"enterprise": {
"parameters": {
"company": "foo",
"companyID": "12345678912345",
"address": "adress principale",
}
},
"contacts": [],
"requirements": []
}
function rec_res(obj) {
return Object.keys(obj).reduce((r, e) => {
if(typeof obj[e] == 'object') Object.assign(r, rec_res(obj[e]))
else r[e] = obj[e];
return r;
}, {})
}
console.log(rec_res(jsonobj))
var jsonobj = {
"gender": "male",
"country": "us",
"phone": "06 12 34 56 78",
"enterprise": {
"parameters": {
"company": "foo",
"companyID": "12345678912345",
"address": "adress principale",
}
},
"contacts": [],
"requirements": []
}
var result=[];
function rec_res(obj_res) {
var foo=[];
$.each(Object.keys(obj_res), function(k, v) {
if (typeof obj_res[v] == "object") {
var data = rec_res(obj_res[v]);
if(data!=undefined && data.length!=0){
data.map(function(d){
result.push(d);
});
}
} else {
result.push({[v]:obj_res[v]});
foo.push({[v]:obj_res[v]});
}
return foo;
});
//console.log(foo);
};
rec_res(jsonobj);
alert(JSON.stringify(result));

Sort by name and number

I have an Array
people = [
{ "name": "Bob Mike", "nickname": "john" , "points": 5 ) },
{ "name": "Andrea Maria", "nickname": "bob", "points": 5 )}
{ "name": "Larry Kiu", "nickname": "larry", "points": 4 ) }
];
I want to sort it like this
Andrea Maria - 5
Bob Mike - 5
Larry Kiu - 4
I'm not into the Sort Method, I found this little Code, but it only Sorts the Points, not including the ASC from the name field.
people.sort(dynamicSort('name')).sort(dynamicSort('points'));
function dynamicSort(property) {return function(a, b) {
return (a[property] > b[property]) ? -1 : (a[property] < b[property])? 1 : 0;
}}
Edit: Thank you Erazihel
You can first sort by points and then use localeCompare to sort by names.
var people = [
{ "name": "Bob Mike", "nickname": "john" , "points": 5 },
{ "name": "Andrea Maria", "nickname": "bob", "points": 5},
{ "name": "Larry Kiu", "nickname": "larry", "points": 4 }
];
people.sort(function(a, b) {
return b.points - a.points || a.name.localeCompare(b.name)
})
console.log(people)
You can use localeCompare to compare two strings
const people = [
{ name: "Bob Mike", nickname: "john" , points: 5 },
{ name: "Andrea Maria", nickname: "bob", points: 5 },
{ name: "Larry Kiu", nickname: "larry", points: 4 }
];
people.sort(function(a, b) {
return b.points - a.points || a.name.localeCompare(b.name)
});
console.log(people.map(a => a.name + ' - ' + a.points));
What you want is something like this
var people = [
{ "name": "Bob Mike", "nickname": "john" , "points": 5 },
{ "name": "Andrea Maria", "nickname": "bob", "points": 5},
{ "name": "Larry Kiu", "nickname": "larry", "points": 4 }
];
people.sort(compare('name')).sort(compare('points'));
function compare(property){
return function(a, b){
return a[property] <= b[property];
}
}
console.log(people);
the compare function returns a function which would work on the property specified
By keeping the idea of a function for checking a single key of the object for sorting, you could use an array with the keys for sorting with their order and build a new array with the callbacks for using with sort.
In the sort callbak, the functions are invoked until the result value is different from a truthy value, which stops the iteration.
function dynamicSort(key, order) {
return function(a, b) {
return +(order === 'ASC' || -1) * +(a[key] < b[key] && -1 || a[key] > b[key]);
};
}
var people = [{ name: "Bob Mike", nickname: "john" , points: 5 }, { name: "Andrea Maria", nickname: "bob", points: 5 }, { name: "Larry Kiu", nickname: "larry", points: 4 }],
sortBy = [['points', 'DESC'], ['name', 'ASC']].map(a => dynamicSort(...a));
people.sort(function (a, b) {
var v = 0;
sortBy.some(f => v = v || f(a, b));
return v;
});
console.log(people);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JavaScript multi-level sorting with multi dimension array

I have a JSON object like below:
[
{
"name": "Robert",
"age":32,
"country": "UK"
},
{
"name": "Prasad",
"age":28,
"country": "India"
},
{
"name": "Benny",
"age":45,
"country": "USA"
},
{
"name": "Robin",
"age":34,
"country": "UK"
},
{
"name": "Bob",
"age":20,
"country": "India"
}
]
I have applied the array sorting for "name" column alone. I want to apply sort for “name” column first and then “age”.
This is how i sort the array by name:
var sort_by = function(field, reverse, primer){
var key = primer ?
function(x) {return primer(x[field])} :
function(x) {return x[field]};
reverse = [-1, 1][+!!reverse];
return function (a, b) {
return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
}
}
Call the sort function:
arrayToSort.sort(
sort_by( “name”, true, function(a){
return a.toUpperCase();
}) );
How can I get the array sorted like below?
[{
"name": "Bob",
"age":20,
"country": "India"
},
{
"name": "Benny",
"age":45,
"country": "USA"
},
{
"name": "Prasad",
"age":28,
"country": "India"
},
{
"name": "Robert",
"age":32,
"country": "UK"
},
{
"name": "Robin",
"age":34,
"country": "UK"
}]
I think what you are looking for is a way to "chain" sort_by(..) calls so as to be able to operate on more than one field.
Below is a slightly modified version of your code. Its pretty much self-explanatory.
arrayToSort = [ ...];
var sort_by = function(field, reverse, primer){
var key = primer ?
function(x) {return primer(x[field]); }:
function(x) {return x[field] };
reverse = [-1, 1][+!!reverse];
return function (a, b) {
a = key(a);
b = key(b);
return a==b ? 0 : reverse * ((a > b) - (b > a));
//^ Return a zero if the two fields are equal!
}
}
var chainSortBy = function(sortByArr) {
return function(a, b) {
for (var i=0; i<sortByArr.length; i++) {
var res = sortByArr[i](a,b);
if (res != 0)
return res; //If the individual sort_by returns a non-zero,
//we found inequality, return the value from the comparator.
}
return 0;
}
}
arrayToSort.sort(
chainSortBy([
sort_by( "name", true, function(a){
return a.toUpperCase();
}),
sort_by("age", true, null)
])
);
console.log(arrayToSort); //Check browser console.
For output: check the JSFiddle
The solution is back to native, just :
function orderByProp(arr,prop){
var order = [], ordered=[];
//create temp ID and Save the real index
for(i=0; i < arr.length;++i){ order.push(arr[i][prop]+"-:-"+i);}
ordered.sort();
for(i=0; i < arr.length;++i){
var val = order.split("-:-");
ordered.push(arr[val[1]]); Get the real array by saved index
}
return ordered;
}
// Apply
var arr = [{
"name": "Bob",
"age":20,
"country": "India"
},
{
"name": "Benny",
"age":45,
"country": "USA"
},
{
"name": "Prasad",
"age":28,
"country": "India"
},
{
"name": "Robert",
"age":32,
"country": "UK"
},
{
"name": "Robin",
"age":34,
"country": "UK"
}];
var sort = orderByProp(arr,"name");
i'm not tested this. but hope it could solve your problems
This is relatively trivial with the Array.sort method by using the || operator, where it will use the second value if the first comparison returns 0, meaning the value was the same:
const data = [
{
"name": "Robert",
"age": 32,
},
{
"name": "David",
"age": 24,
},
{
"name": "Robert",
"age": 28,
},
];
const sortedData = data.sort((a, b) => a.name.localeCompare(b.name) || a.age - b.age);
console.log(sortedData);
Credit for this goes to #NinaScholz for her answer here.

Categories