How to implement a reduce correctly? - javascript

I am getting a table with data coming from an array, but there was some data that was repeated and I was advised to do a reduce.
The data arrives well, but there are some that I cannot obtain.
This is what I'm trying to implement, from what I understand about reduce, accumulate the array and then do the traversal
this.headerVariedades = response['objParams'].monthlyTitle;
const aux = response['objParams'].monthlyDetail.reduce((acc, curr: {
mesTemporada: string,
codigo_producto: number,
monto_moneda_norm: number
}) => {
if (acc[curr.mesTemporada]) {
acc[curr.mesTemporada][curr.codigo_producto] = curr.monto_moneda_norm;
} else {
const aux = {
mesTemporada: curr.mesTemporada
};
aux[curr.codigo_producto] = curr.monto_moneda_norm;
acc[curr.mesTemporada] = aux;
}
return acc;
}, {});
this.listadoResultado = Object.values(aux);
As seen in the image I need to fill in the "fecha" and "fecHas"
This is what you get when you check with postman.
"weeklyTitle": [
{
"codigo": 27,
"pro_des": "Maíz Amarillo, FOB Puerto Argentino",
"gru_cod": 2
},
{
"codigo": 18,
"pro_des": "Maíz Yellow N° 2, FOB Golfo, USA",
"gru_cod": 2
},
{
"codigo": 51,
"pro_des": "Maíz Yellow N° 3, FOB Golfo, USA",
"gru_cod": 2
}
],
"weeklyDetail": [
{
"semana": 20,
"temporada": 2021,
"fecha": 1621224000000,
"fecHas": 1621742400000,
"codigo_producto": 18,
"monto_moneda_norm": 305.4700
},
{
"semana": 20,
"temporada": 2021,
"fecha": 1621224000000,
"fecHas": 1621742400000,
"codigo_producto": 27,
"monto_moneda_norm": 259.8000
},
{
"semana": 20,
"temporada": 2021,
"fecha": 1621224000000,
"fecHas": 1621742400000,
"codigo_producto": 51,
"monto_moneda_norm": 304.4700
},
{
"semana": 21,
"temporada": 2021,
"fecha": 1621828800000,
"fecHas": 1622347200000,
"codigo_producto": 18,
"monto_moneda_norm": 299.3500
},
{
"semana": 21,
"temporada": 2021,
"fecha": 1621828800000,
"fecHas": 1622347200000,
"codigo_producto": 27,
"monto_moneda_norm": 254.3333
},
{
"semana": 21,
"temporada": 2021,
"fecha": 1621828800000,
"fecHas": 1622347200000,
"codigo_producto": 51,
"monto_moneda_norm": 298.3500
}
],
In this way I am assembling the table, as I receive the header in another array I do a forEach.
this.columnasGrilla.push({
header: 'Semana ',
field: 'semana'
});
this.columnasGrilla.push({
header: 'Desde',
field: 'fecha'
});
this.columnasGrilla.push({
header: 'Hasta',
field: 'fecHas'
});
this.headerVariedades.forEach((item) => {
this.columnasGrilla.push({
header: item.pro_des,
field: 'item.codigo'
})
})
This is the example that repeat values. This happens when I choose more variety of a product.
in this case I don't use the reduce . I just call it this way
this.listadoResultado = response['objParams'].weeklyDetail;
that's why I was recommended to use reduce to sort the table in these cases

Related

Calculating the average time based on total on a Array

Using Lodash and moment packages.
I have a array with objects. Each has a 2 important properties.
Assume the trades are like this
const trades = [
{ RealizedPnL: 20, OpenTradeAt: '2023-02-16T19:04:16.2163527' },
{ RealizedPnL: 30, OpenTradeAt: '2023-01-23T13:45:32.4256713' },
{ RealizedPnL: 10, OpenTradeAt: '2023-02-03T08:12:01.0152846' },
{ RealizedPnL: -15, OpenTradeAt: '2023-01-10T16:39:52.1198765' },
{ RealizedPnL: -5, OpenTradeAt: '2023-02-24T11:27:56.3297178' },
{ RealizedPnL: -5, OpenTradeAt: '2023-02-24T11:27:56.3297178' },
{ RealizedPnL: 25, OpenTradeAt: '2023-02-26T13:17:56.3297128' },
{ RealizedPnL: 60, OpenTradeAt: '2023-02-24T17:27:56.3297178' }
];
I want to know based on this information whetever the best time is to take a trade in average. Let's say there are 4 very good trades (positive RealizedPnL) at around 17:00 and there is 2 (positive RealizedPnL) trades at arround 20:00 then the best trade in average shall be 17:00 in this case.
This is basically a indication for which time the trades is best to open based on information from the past.
I want to actually know both the time which the trader is best to open a trade at and the worst time to trade but I want to focus on best time first.
What I already got:
const timeRegex = /(\d{2}):(\d{2}):(\d{2})/;
const times = _.map(trades, (trade) => {
const [, hours, minutes] = trade.OpenTradeAt.match(timeRegex);
return { time: `${hours}:${minutes}`, PnL: trade.RealizedPnL };
});
Result:
[
{
"time": "19:04",
"PnL": 20
},
{
"time": "13:45",
"PnL": 30
},
{
"time": "08:12",
"PnL": 10
},
{
"time": "16:39",
"PnL": -15
},
{
"time": "11:27",
"PnL": -5
},
{
"time": "11:27",
"PnL": -5
},
{
"time": "13:17",
"PnL": 25
},
{
"time": "17:27",
"PnL": 60
}
]
Is this even possible to know with these information?

objects with same key values data

I have an object structured like this
{
"Mental Ability": [
{
"_id": "1",
"exams": [
{
"_e": "12",
"years": [
{
"y": 2012,
"freq": 3
},
{
"y": 2011,
"freq": 3
}
]
}
]
},
{
"_id": "2",
"exams": [
{
"_e": "12",
"years": [
{
"y": 2011,
"ques_freq": 3
},
{
"y": 2012,
"freq": 1
}
]
}
]
}
]
}
I want data like in Mental Ability inside exams years it should display like
'y': 2012
'freq': 4,
'y': 2011
'freq': 6
means the same years freq should be added and displayed
like those exams years who are in y 2011 there frequency should be summed and displayed
I suppose "ques_freq" is a typo for "freq", and o below is the object you mentioned.
function count(mentalAbility) {
let out = {};
mentalAbility.forEach(item => {
item.exams.forEach(exam => {
exam.years.forEach(year => {
out[year.y] === undefined
? (out[year.y] = year.freq)
: (out[year.y] += year.freq);
});
});
});
return out;
}
console.log(count(o['Mental Ability'])); // { '2011': 6, '2012': 4 }
function format(counts) {
return Object.keys(counts).reduce((acc, year) => {
acc.push({
y: year,
freq: counts[year]
});
return acc;
}, []);
}
console.log(format(count(o['Mental Ability']))); // [ { y: '2011', freq: 6 }, { y: '2012', freq: 4 } ]
Please note that I do not add any undefined / null checking for the field lookups

Sort array by child's child

The situation is: I have an array of objects, in which every object has an array of objects. The array looks like this:
[
{
"dislikes": [
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511001,
"timezoneOffset": -60,
"year": 118
},
},
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511008,
"timezoneOffset": -60,
"year": 118
},
}
],
},
{
"dislikes": [
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511011,
"timezoneOffset": -60,
"year": 118
},
},
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511028,
"timezoneOffset": -60,
"year": 118
},
}
],
}
]
So I want to sort the users, and the dislikes by the time in their dislikes. So the user with the earliest dislike would be first, as well as the earliest dislike would be first in each users' dislikes array. I believe I have to do multiple sorts, but how can I do that exactly?
You can map the items and add a property to it containing the earliest dislike and then sort on that:
const data = [{"dislikes":[{"createDate":{"date":11,"day":0,"hours":18,"minutes":15,"month":10,"seconds":11,"time":1541956511001,"timezoneOffset":-60,"year":118}},{"createDate":{"date":11,"day":0,"hours":18,"minutes":15,"month":10,"seconds":11,"time":1541956511008,"timezoneOffset":-60,"year":118}}]},{"dislikes":[{"createDate":{"date":11,"day":0,"hours":18,"minutes":15,"month":10,"seconds":11,"time":1541956511011,"timezoneOffset":-60,"year":118}},{"createDate":{"date":11,"day":0,"hours":18,"minutes":15,"month":10,"seconds":11,"time":1541956511028,"timezoneOffset":-60,"year":118}}]}];
console.log(
data
//map and add newestDislike property
.map((d) => ({
...d,
//reduce and only takes the lowest time value
newestDislike: (d.dislikes || []).reduce(
(result, item) =>
item.createDate.time < result
? item.createDate.time
: result,
Infinity, //defaults to infinity (if no dislikes)
),
}))
.sort((a, b) => a.newestDislike - b.newestDislike),
);
If the dislikes in the user are already sorted by oldest date first then you can skip the map and reduce part. If a user can have empty dislikes or undefined then make sure you use a getter function with a default so your code won't crash:
//gets a nested prop from object or returns defaultValue
const get = (o = {}, path, defaultValue) => {
const recur = (o, path, defaultValue) => {
if (o === undefined) return defaultValue;
if (path.length === 0) return o;
if (!(path[0] in o)) return defaultValue;
return recur(o[path[0]], path.slice(1), defaultValue);
};
return recur(o, path, defaultValue);
};
console.log(
data.sort(
(a, b) =>
get(
a,
['dislikes', 0, 'createDate', 'time'],
Infinity,
) -
get(
b,
['dislikes', 0, 'createDate', 'time'],
Infinity,
),
),
);
//Supply the array you've metioned as the argument users to the below method, sortDislikesForAllUsers
let sortDislikesForAllUsers = function(users) {
return users.map(user => {
return {
dislikes: user.dislikes.sort((dislikeA, dislikeB) => ((dislikeA.createDate.time < dislikeB.createDate.time) ? -1 : (dislikeA.createDate.time > dislikeB.createDate.time) ? 1 : 0))
}
})
}
//Supply the array returned in the above method as input to the below method, sortUsers
let sortUsers = function(arrayOfSortedDislikesPerUser) {
return arrayOfSortedDislikesPerUser.sort((userA, userB) => ((userA.dislikes[0].createDate.time < userB.dislikes[0].createDate.time) ? -1 : (userA.dislikes[0].createDate.time > userB.dislikes[0].createDate.time) ? 1 : 0))
}
let arrayOfSortedDislikesPerUser = sortDislikesForAllUsers(users);
let finalSortedArray = sortUsers(arrayOfSortedDislikesPerUser);
console.log(finalSortedArray);
In the below snippet,
sortDislikesForAllUsers This method sorts the dislikes for individual
users
sortUsers This method sorts the users based on the first dislike time
of the sorted dislikes array obtained from the above method
Simple :)
Run the below snippet. You can directly copy paste it in your code!
let users = [{
"dislikes": [
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511001,
"timezoneOffset": -60,
"year": 118
},
},
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511008,
"timezoneOffset": -60,
"year": 118
},
}
],
},
{
"dislikes": [
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511011,
"timezoneOffset": -60,
"year": 118
},
},
{
"createDate": {
"date": 11,
"day": 0,
"hours": 18,
"minutes": 15,
"month": 10,
"seconds": 11,
"time": 1541956511028,
"timezoneOffset": -60,
"year": 118
},
}
],
}]
let sortDislikesForAllUsers = function(users) {
return users.map(user => {
return {
dislikes: user.dislikes.sort((dislikeA, dislikeB) => ((dislikeA.createDate.time < dislikeB.createDate.time) ? -1 : (dislikeA.createDate.time > dislikeB.createDate.time) ? 1 : 0))
}
})
}
let sortUsers = function(arrayOfSortedDislikesPerUser) {
return arrayOfSortedDislikesPerUser.sort((userA, userB) => ((userA.dislikes[0].createDate.time < userB.dislikes[0].createDate.time) ? -1 : (userA.dislikes[0].createDate.time > userB.dislikes[0].createDate.time) ? 1 : 0))
}
let arrayOfSortedDislikesPerUser = sortDislikesForAllUsers(users);
let finalSortedArray = sortUsers(arrayOfSortedDislikesPerUser);
console.log(finalSortedArray);
EDIT: WRT to the comment by #HMR:
1. It mutates the original array. Yes. If you want to avoid mutation, you must create a copy of the sent array.
let noRefCopy = new Array()
noRefCopy = noRefCopy.concat(originalArr)
Now, perform sorting on the copy and return the same.
2. If you wanna have checks for undefined etc, sure you can.
The above answer attempts to address the logic. Sure we can address the above 2 concerns if the question is really specific to them.
Cheers,
Kruthika
Check the code below. This will let you sort based on time:
function sortByTime(obj1, obj2){
return obj1.time - obj2.time;
}
array.sort((obj1, obj2)=>{
obj1.dislikes.sort(sortByTime);
obj2.dislikes.sort(sortByTime);
return obj1.dislikes[0].time - obj2.dislikes[0].time;
});
I did not get what you meant by earliest time. The above code sorts time in ascending order.
NOTE: The above code does not handle edge cases where a property night be missing
Something like as follows (with lodash.js)
_.each(users, (u) => { u.dislikes = _.sortBy(u.dislikes, 'createdDate.time'); });
users = _.sortBy(users, 'dislikes[0].createdDate.time');

Group sum and transform json object with values in nested array

I am trying to aggregate and transform the following json :
[
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}]
into an object that is grouped by itemId, and then aggregated by quantity, and aggregated by total cost (item cost * item quantity for each order) by month. Example:
[
{
"itemId": 100,
"period": [
{
"month": "01/17",
"quantity": 12,
"cost": 130
}
]
},
{
"itemId": 101,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 100
},
{
"month": "02/17",
"quantity": 5,
"cost": 100
}
]
},
{
"itemId": 102,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 125
},
{
"month": "02/17",
"quantity": 1,
"cost": 25
}
]
}
]
I have a small indention on my desk in which I have been beating my head trying to figure how to do this using native map/reduce or lodash.
You can do like this:
var orders = [{orderId:"01",date:"2017-01-02T06:00:00.000Z",items:[{itemId:100,itemCost:12,itemQuantity:10},{itemId:102,itemCost:25,itemQuantity:4}]},{orderId:"02",date:"2017-01-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]},{orderId:"03",date:"2017-02-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]}];
// First, map your orders by items
var items = {};
orders.forEach(function(order) {
// set the month of each order
var month = new Date(order.date);
month = ('0' + (month.getMonth() + 1)).slice(-2) + '/' + String(month.getFullYear()).slice(-2);
// for each item in this order
order.items.forEach(function(item) {
// here we already have both keys: "id" and "month"
// then, we make sure they have an object to match
var id = item.itemId;
if (!items[id]) {
items[id] = {};
}
if (!items[id][month]) {
items[id][month] = { cost:0, quantity:0 };
}
// keep calculating the total cost
items[id][month].cost += item.itemCost * item.itemQuantity;
items[id][month].quantity += item.itemQuantity;
});
});
// Now, we format the calculated values to your required output:
var result = Object.keys(items).map(function(id) {
var obj = {
itemId: id,
period: Object.keys(items[id]).map(function(month) {
items[id][month].month = month;
return items[id][month];
}),
};
return obj;
});
console.log(result);
Hope it helps.
You could use this transformation:
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
const myList = [
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}];
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think the other answers out there do a pretty good job from the vanilla angle, so I wanted to take a stab at a more lodash-intensive approach since you mentioned it as a tag. This is mainly just a fun challenge, but I hope the solution is elegant enough for you to lift components from.
Before we begin, I'll be using both the vanilla lodash module and the functional programming flavor of lodash. Let fp be the functional programming module and _ be vanilla (and let orders be your original data structure). Also, as a challenge, I'll do my best to minimize vanilla JS methods and arrow funcs to maximize lodash methods and function creation methods.
First, let's get all the items in a row, paired with their order information:
const items = _.flatMap(orders, o=> _.map(o.items, i=> [i, o]));
I know I said I wanted to minimize arrow functions, but I couldn't think of any other way to get the order object to the end of the chain. Challenge yourself to rewrite the above in terms of a composition (e.g. fp.compose or _.flow) and see what happens.
I'd say now's as good a time as any to group up our pairs by the item id:
const id_to_orders = _.groupBy(items, fp.get('[0].itemId'));
Here, fp.get('[0].itemId') gives us a function which, given an array, returns the itemId of the first element (in our case, we have a list of pairs, the first element of which is the item, the second of which is the relevant order object). Therefore, id_to_orders is a map from an item's ID to a list of all the times it was ordered.
This id_to_orders map looks pretty close to the data structure we're after. At a high level, all that's left is transforming the order data for each item into the quantity and cost, grouped by month.
const result = _.mapValues(id_map, fp.flow(
// Arrange the item's orders into groups by month
fp.groupBy(month)
// We're done with the order objects, so fp.get('[0]') filters them
// out, and the second function pairs the item's cost and quantity
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
// Sum up the cost (left) and quantity (right) for the item for the month
, fp.reduce(add_pair, [0, 0])))
// These last couple lines just transform the resulting data to look
// closer to the desired structure.
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count}))
));
And the helpers month and add_pair referenced above:
function month([item, order]){
const date = new Date(order.date)
, month = date.getMonth() + 1
, year = date.getFullYear().toString().slice(-2);
return `${month}/${year}`;
}
function add_pair(p1, p2){
return [p1[0] + p2[0], p1[1] + p2[1]];
}
Just out of curiosity (or sadism), let's see what this whole thing would look like chained together as a single pipeline:
const get_order_data = fp.flow(
fp.flatMap(o=> _.map(o.items, i=> [i, o]))
, fp.groupBy(fp.get('[0].itemId'))
, fp.mapValues(fp.flow(
fp.groupBy(month)
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
, fp.reduce(add_pair, [0, 0])))
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count})))
));
const result = get_order_data(orders);
You'll notice this composed version has a lot more fp (as opposed to _). If you're curious why it's easier this way, I encourage you to read the lodash FP guide.
jsfiddle with everything.
Finally, if you'd like to transform the result from the code above exactly into the output format you mentioned in your post, here's what I recommend:
const formatted = _.keys(result).map(k=> ({itemId: k, periods: result[k]}));

Is it possible to pass a custom comparator to lodash's sortBy function?

For example, I want to sort with respect to Intl.Collator().compare. Is there any way to pass this comparator to be used by _.sortBy?
You can use lodash mixin's
_.mixin({
sortWith : function(arr, customFn) {
return _.map(arr).sort(customFn)
}
});
You can now do
_.sortWith(array, function(a, b) {
//custom function that returns either -1, 0, or 1 if a is <, ==, or > than b
});
You can now chain this like:
_.chain(myObject)
.get('some_array_property')
.sortWith(function(a, b) {
//determine if a <=> b
})
.value();
Internally, sortWith maps the array to a new array so that it doesn't modify the array passed into it and uses the native sort() method.
No, unfortunately this is not currently possible.
A workaround is to use the iteratees function to map the values to something the standard comparator will sort correctly. This is however almost never practical.
It's also asked for here https://github.com/lodash/lodash/issues/246, but no response from the author.
This is enough for simple ordering based on finite values.
const MAP = {
BRONZE: 1,
SILVER: 2,
GOLD: 3,
PLATINUM: 4,
}
const DATA = [
{ name: 'A', type: 'SILVER' },
{ name: 'B', type: 'BRONZE' },
{ name: 'C', type: 'PLATINUM' },
{ name: 'F', type: 'SILVER' },
{ name: 'G', type: 'GOLD' },
{ name: 'H', type: 'BRONZE' },
]
_.sortBy(DATA, (item) => MAP[item.type])
Result:
[
{"name":"B","type":"BRONZE"},
{"name":"H","type":"BRONZE"},
{"name":"A","type":"SILVER"},
{"name":"F","type":"SILVER"},
{"name":"G","type":"GOLD"},
{"name":"C","type":"PLATINUM"}
]
Not exactly sure what you are looking for. But if you are finding ways to use comparator in lodash sort, this may help:
Using _.chain() to get lodash collect will enable you to pass in comparator to sort()
console.log(JSON.stringify(_.sortBy(res, lat))); // sortBy doesn't take in comparator
console.log(
JSON.stringify(
_.chain(res)
.sort((a, b) => b.lat - a.lat) // sort takes in comparator
.value()
)
);
Not lodash, but might come in handy for someone looking for native sort.
var customSort = (
selector,
options,
locales = undefined,
) => (a, b) => {
return selector(a).localeCompare(selector(b), locales, {numeric: true,...options});
};
var x = [
{ name: '1-test' },
{ name: '01-test' },
{ name: '11-test' },
{ name: '11-Test' },
{ name: '10-test' },
{ name: '40-btest' },
{ name: '40-ctest' },
{ name: '40-atest' },
{ name: '2-test' },
{ name: '20-test' },
{ name: 'ätest' },
{ name: 'atest' },
];
console.log(x.sort(customSort((x) => x.name)));
console.log(x.sort(customSort((x) => x.name, { caseFirst: 'upper' })));
// in Swedish
console.log(x.sort(customSort((x) => x.name, { sensitivity: 'base' },'sv')));
Options come from:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
As said by other answers, you cannot pass in a comparator to _.sortBy like in Array.prototype.sort().
One workaround would be to add a new calculated property to the objects which would be the ordering and then use _.sortBy on that.
So if you had a list of objects like [{name: "hello there"}, {name: "world"}] but you wanted to sort them by name length, you could do:
_(arr)
//augment each object with a calculated `order` property
.map(obj => ({...obj, order: obj.name.length}))
.sortBy('order')
.value()
Result: [{name: "world", order: 5}, {name: "hello there", order: 11}]
Simple implementaion for lodash/fp (functional programming lodash) with currying (not restricted to lodash actually):
const sortWith = comparator => list => list.map(i => i).sort(comparator);
And for TypeScript:
type ComparatorFn<T> = (a: T, b: T) => number;
const sortWith = <P>(comparator: ComparatorFn<P>) => (list: P[]): P[] => list.map(i => i).sort(comparator);
Found the corresponding issue, here is stated that the mentioned feature is already merged long ago but unfortunately not released yet. https://github.com/lodash/lodash/pull/3764. This would be really great to have it available
actually iteratees can be mapping for the return result:
const users = [
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 36 },
{ 'user': 'fred', 'age': 40 },
{ 'user': 'barney', 'age': 34 }
];
_.sortBy(users, [function(o) { return o.user; }]);
// output: objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
or iteratees can be normal js sort function like i made in the below example to sort array of objects to sort cards when cardsStatus === 'NORI' so card should be on top of array
const cardsBeforeSort = [
{
"cardStatus": "NORM",
"consumedLimit": 0,
"cardAccountSerial": "10551880",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": -9,
"key": "405433******8106"
},
{
"cardStatus": "NORI",
"consumedLimit": 0,
"cardAccountSerial": "10551908",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "405433******8382"
},
{
"cardStatus": "HOLD",
"consumedLimit": -169122.81,
"cardAccountSerial": "10548192",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdjj",
"nickName": "",
"aan": "123",
"balance": 5579.29,
"key": "417323******3321"
},
{
"cardStatus": "NORI",
"consumedLimit": -7.74,
"cardAccountSerial": "10549814",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "429927******1548"
}
]
const sortedCards = sortBy(userCards, [
(first, second) =>
first.cardStatus === 'NORI' ? -1 : second === 'NORI' ? 1 : 0,
]);
this will result in the following output:
console.log(sortedCards);
[
{
"cardStatus": "NORI",
"consumedLimit": -7.74,
"cardAccountSerial": "10549814",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "429927******1548"
},
{
"cardStatus": "NORI",
"consumedLimit": 0,
"cardAccountSerial": "10551908",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": 1,
"key": "405433******8382"
},
{
"cardStatus": "NORM",
"consumedLimit": 0,
"cardAccountSerial": "10551880",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdhh",
"nickName": "",
"aan": "123",
"balance": -9,
"key": "405433******8106"
},
{
"cardStatus": "HOLD",
"consumedLimit": -169122.81,
"cardAccountSerial": "10548192",
"cashLimit": null,
"applePayStatus": "ELIGIBLE",
"embossName": "Hdjj",
"nickName": "",
"aan": "123",
"balance": 5579.29,
"key": "417323******3321"
},
]
actually the benefit of using sortBy lodash function is being functional programming immutable solution because of not mutating cardsBeforeSort array

Categories

Resources