Lodash custom orderBy then by - javascript

I need to use lodash to implement a custom sort based on classification, then by creation time, with more recently created items with the same classification having a lower index in the array. I'm pretty sure I need to use orderBy, but I don't know how to add the date sorting part. This is what I have:
let sorted = _.orderBy(this.followed_requests, function(element){
const rank = { "INCOMPLETE.NEW": 1,
"INCOMPLETE.IN_PROGRESS":2,
"INCOMPLETE.LATER":3}
return rank[element["status"]]})
I'd like my output to have something like:
sorted = [{status:"INCOMPLETE.NEW", created_at: "2021-05-14T14:48:00.000Z"},
{status:"INCOMPLETE.NEW", created_at: "2021-05-13T14:48:00.000Z"},
{status:"INCOMPLETE.IN_PROGRESS", created_at: "2021-05-14T14:48:00.000Z"},
{status:"INCOMPLETE.IN_PROGRESS", created_at: "2021-05-13T14:48:00.000Z"},
{status:"INCOMPLETE.LATER", created_at: "2021-05-14T14:48:00.000Z"},
{status:"INCOMPLETE.NEW", created_at: "2021-05-13T14:48:00.000Z"} ]

orderBy accepts an array of functions and sort directions. There's multiple ways this could be implemented, but since both sort values require transformation this seems like a good option.
const reqs = [
{ status: "INCOMPLETE.LATER", created_at: "2021-05-14T14:48:00.000Z" },
{ status: "INCOMPLETE.NEW", created_at: "2021-05-13T15:48:00.000Z" },
{ status: "INCOMPLETE.IN_PROGRESS", created_at: "2021-05-14T14:48:00.000Z" },
{ status: "INCOMPLETE.IN_PROGRESS", created_at: "2021-05-13T15:48:00.000Z" },
{ status: "INCOMPLETE.NEW", created_at: "2021-05-14T16:48:00.000Z" },
{ status: "INCOMPLETE.NEW", created_at: "2021-05-13T14:48:00.000Z" },
]
const rank = {
"INCOMPLETE.NEW": 1,
"INCOMPLETE.IN_PROGRESS": 2,
"INCOMPLETE.LATER": 3,
}
let sorted = _.orderBy(reqs, [
i => rank[i.status],
i => new Date(i.created_at)
], ["asc", "asc"])
console.log(sorted)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Related

Arrange Chat Messages By Date

I am working with Vue, but this is a general javascript question.
I make a call to an api to get cat messages for a chat UI. The initial call returns an array of objects where each object is a chat message object as below.
data: [
0: {id: 1, created_at: "2022-05-20T15:12:40.000000Z", updated_at: "2022-05-20T17:18:03.000000Z",…}
1: {id: 2, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…}
2: {id: 3, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…}
3: {id: 4, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…}
]
I initial wanted to format the message so they can be grouped by their dates in the chat window. This is the code I used to group them
This is a computed property in vue
const formattedChats = computed(() => {
let dateFormattedMessages = messages.value.map(message => {
return {...message, updated_at: new Date(message.updated_at).toDateString(), created_at: new Date(message.created_at).toDateString()}
})
return dateFormattedMessages.reduce((total, currentValue) => {
total[currentValue.updated_at] = total[currentValue.updated_at] || [];
total[currentValue.updated_at].push(currentValue);
return total;
}, Object.create(null));
})
The above will first take the each chat object an convert their updated_at and created_at to a date string and then group the array using the updated_at.
The result was as follows:
formattedChats = {
Fri Jun 24 2022: [
{...}, {...
]
Fri May 20 2022:[
{...}, {...
]
Mon Jun 27 2022:Array[
{...}, {...
]
Sat May 21 2022:Array[
{...}, {...
]
Tue Jun 28 2022:Array[
{...}, {...
]
}
If you notice, the problem I am facing is that the dates are not arranged in any order. it doesnt make sense to render it to the UI like this because the resulting chats with not be arranged by date.
This is how the UI should look
You need to use arrays (or a map) to keep the order sorted. I would use an array of arrays. First you need to sort the data on date (I used updated_at, which I made of type Date in the data array). Then iterate over the sorted array. See snippet below.
const data = [{
id: 1,
updated_at: new Date("2022-05-21T15:12:40.000000Z"),
created_at: "2022-05-20T17:18:03.000000Z"
},
{
id: 2,
updated_at: new Date("2022-05-20T15:12:41.000000Z"),
created_at: "2022-05-20T17:18:04.000000Z"
},
{
id: 3,
updated_at: new Date("2022-05-23T15:12:41.000000Z"),
created_at: "2022-05-20T17:18:04.000000Z"
},
{
id: 4,
updated_at: new Date("2022-05-21T15:12:41.000000Z"),
created_at: "2022-05-20T17:18:04.000000Z"
},
]
const sortedData = data.sort(
(a, b) => Number(a.updated_at) - Number(b.updated_at),
);
let currentDay = sortedData[0].updated_at;
const stillCurrentDay = (dayOfMessage) => {
return dayOfMessage.getFullYear() === currentDay.getFullYear() &&
dayOfMessage.getMonth() === currentDay.getMonth() &&
dayOfMessage.getDate() === currentDay.getDate()
}
let dayMessageArray = [];
const fullMessageArray = [];
const createMessagesArray = (messages) => {
const newDay = {};
newDay[currentDay.toISOString().split('T')[0]] = messages;
fullMessageArray.push(newDay);
}
sortedData.forEach(message => {
if (!stillCurrentDay(message.updated_at)) {
createMessagesArray(dayMessageArray);
currentDay = message.updated_at;
dayMessageArray = [];
}
dayMessageArray.push(message);
});
createMessagesArray(dayMessageArray);
console.log(fullMessageArray);
Hope this helps. Perhaps there are easier ways, please let me know.

searching list of objects by key and returning count

I am using MongoDB with a Node API and one route needs to return a summary count of each type in a collection.
I am not using the MongoDB Aggregate pipelines because the data I need has already been sent to the API for other summary statistics in the same route.
Note: I have put the _id's below in single quotes for ease of use but they are mongoose.Schema.Types.ObjectId's.
So, given that I have an array of mongo objects like this:
const allObs = [
{
_id: '60d5f37fd93fb82ebe84d920',
type: '60d5f1d4cdc8942dc5b6b12e',
otherFields: 'about 10 - removed for clarity'
},
{
_id: '60d5f389d93fb82ebe84d926',
type: '60d5f1d4cdc8942dc5b6b12e',
otherFields: 'ditto'
},
{
_id: '60d5f39bd93fb82ebe84d92c',
type: '60d5f1e3cdc8942dc5b6b138',
otherFields: 'foobarbarfoo'
}
]
and I have a lookup table like this...
const lookupTable = [
{ _id: '60d5f1d4cdc8942dc5b6b12e', type: 'duck' },
{ _id: '60d5f1decdc8942dc5b6b133', type: 'goose' },
{ _id: '60d5f1e3cdc8942dc5b6b138', type: 'crane' },
{ _id: '60d5f1e9cdc8942dc5b6b13d', type: 'heron' }
]
How can I go about creating a summary table like this?
[
{ name: 'duck', data: [2] },
{ name: 'crane', data: [1] }
]
The resulting table structure is a bit odd (data with single value arrays) but we need this structure for Apex Charts.
Any help would be great, thank you.
There are multiple ways to do this, but the basic logic is doing a groupBy and match with lookup table. It would be easier to do with lodash or a helper library. But also without using JS it can be done pretty easily.
For a quick solution u can use this:
//Group by type and then storing the count
const grouped = allObs.reduce((p, c) => {
p[c.type] = p[c.type] || 0;
p[c.type] += 1;
return p;
}, {});
// putting that into a result array.
const result = lookupTable
.filter(entry=>grouped[entry._id]) //filtering whatever is not there
.map(entry => {
return { name: entry.type, data: [grouped[entry._id]] }
});
You can do it in single pass using a good old for loop.
Output:
[ { name: 'duck', data: [ 2 ] }, { name: 'crane', data: [ 1 ] } ]

Remove duplicate elements based on date field in javascript

I want a function that takes an array and filters out old duplicates.
Specifically, if duplicate ids exist in myList, keep only the object with the newest date. Given the following array
let myList = [{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-21 21:04:13"
},
{
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
},
{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
the function should return:
[{
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
},
{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
You can use the function reduce to build the desired output.
let myList = [{ id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-21 21:04:13"},{ id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5", date: "2018-02-22 21:04:13"},{ id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-23 21:04:13"}];
let result = Object.values(myList.reduce((a, {id, date}) => {
if (a[id]) {
if (a[id].date < date) a[id] = {id, date};
} else a[id] = {id, date};
return a;
}, {}));
console.log(result);
Put the entries into a hash table keyed by id. Each time you add an entry, look up the id and either keep the existing entry or replace it with the new one, based on whichever has a more recent date.
Map and Array.prototype.map() can be combined to functionally filter key based duplicates from arrays.
Array.prototype.sort() can be leveraged to guarantee order.
See below for a practical example.
// Input.
const input = [
{id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-21 21:04:13"},
{id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5", date: "2018-02-22 21:04:13"},
{id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-23 21:04:13"}
]
// Sort By Date.
const sortDate = array => array.sort((A, B) => new Date(A.date)*1 - new Date(B.date)*1)
// Filter Duplicates.
const filter = array => [...new Map(array.map(x => [x.id, x])).values()]
// Output.
const outputRaw = filter(input) // No guaranteed order.
const outputSorted = sortDate(filter(sortDate(input))) // Guaranteed latest.
// Proof.
console.log('Raw', outputRaw)
console.log('Sorted', outputSorted)
This isn't the best answer, just another take on #Ele's solution offered for completeness. Instead of plucking the values after the unique set is found, it works on the returned array for each iteration. The find during each iteration should be less efficient than a key lookup, which is one of the reasons it's not the best answer.
let myList = [{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-21 21:04:13"
}, {
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
}, {
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
let result = myList.reduce((arr, { id, date }) => {
let found = arr.find(v=>v.id==id)
if (found) {
if (found.date < date)
found.date = date
}
else
arr.push({ id, date });
return arr;
}, []);
console.log(result);

Underscore.js - How to sortby a nested array of objects

I have the following javascript object:
var appointments = [];
appointments.push(
{ id: '101',
status: 'accepted',
appointmentTimes: [ { from: '2016-10-28 12:00', to: '2016-10-28 13:00' } ] },
{ id: '102',
status: 'pending',
appointmentTimes: [ { from: '2016-10-24 12:00', to: '2016-10-24 13:00' },
{ from: '2016-10-24 15:00', to: '2016-10-24 16:00' } ] });
I want to sort the array objects using the first item in the appointmentTimes array using the from property so that each object appears in ascending date order.
So in the above example, object with id 102 would appear first in the list. Note each item in appointmentTimes array is already in ascending order.
I've tried the following but it doesnt work:
_.sortBy(appointments, function(appointments) {
return appointments.appointmentTimes.from;
});
Managed to fix:
_.sortBy(appointments, function(appointment) {
return appointment.appointmentTimes[0].from;
});

JSON object - Accessing Values in React Native

In my React Native application I am using the RNDBModels package that is a wrapper over AsyncStorage. Currently I am saving a JSON object through RNDBModels and that works correctly, however accessing the data is proving challenging.
When the code is return from the get method, it is return inside a JSON Object and I would essentially like the values from the result, so that I can iterate over it for a list.
The returned result:
{
'1':
{
name: 'Galaxy',
description: '20gram bars',
_id: 1
},
'2':
{
name: 'Snickers',
description: 'Hazelnuts',
count: 2,
_id: 2
}
}
And the desired outcome so that I can easily iterate over the objects in the array and then render a list in React Native.
[
{
name: 'Galaxy',
description: '20gram bars',
_id: 1
},
{
name: 'Snickers',
description: 'Hazelnuts',
count: 2,
_id: 2
}
]
Any suggestions at accessing the values? I have tried using Object.keys and then subsequently Object.values to no avail sadly.
You can do it with the in operator :
const data = {
'1': {
name: 'Galaxy',
description: '20gram bars',
_id: 1
},
'2': {
name: 'Snickers',
description: 'Hazelnuts',
count: 2,
_id: 2
}
};
var array = [];
for (let prop in data) {
array.push(data[prop]);
}
console.log(array);
JSFiddle
If you're using lodash just one line of code would work for your purpose
_.values(YOUR_OBJECT);
https://lodash.com/docs#values
It will make an array of values from your object.

Categories