I have an Array of Objects like this:
[{
date: 2022-04-07T01:00:00.000+00:00,
type: 'profit'
amount: 200
},{
date: 2022-04-07T01:00:00.000+00:00,
type: 'withdraw'
amount: 600
},{
date: 2022-04-07T01:00:00.000+00:00,
type: 'invest'
amount: 900
},{
date: 2022-04-08T01:00:00.000+00:00,
type: 'deposit'
amount: 200
},{
date: 2022-04-08T01:00:00.000+00:00,
type: 'profit'
amount: 200
}]
In the datasource the dates are not in order so I am sorting by date like this:
this.allTransactions.sort((a: any, b: any) => {
return new Date(b.date).getTime() - new Date(a.date).getTime();
});
However I need to also sort any objects with the same date by an external array:
['invest', 'withdraw', 'profit']
I'm struggling with the second part sorting the date groups already sorted by the order in the array. this.allTransactions is in an *ngFor loop in Angular 12
You can add an extra condition after your current one, which will be used if the first one resolves as 0
const typeOrder = ['invest', 'withdraw', 'profit']
transactions.sort((a, b) => {
return new Date(b.date).getTime() - new Date(a.date).getTime() || typeOrder.indexOf(a.type) - typeOrder.indexOf(b.type)
});
Related
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.
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>
I am working with the object like this:-
{
0:
buyAmount: 16.664328043964396
buyAmountInUsd: 16.685266204095775
date: {date: '2021-12-07'}
sellAmount: {USD: 500, INR: 35000}
1:
buyAmount: 1004.7015442959262
buyAmountInUsd: 1005.9639175379324
date: {date: '2021-12-07'}
sellAmount: {USD: 1000, INR: 79000}
......
and I am trying to make a new array using useState hook with this data but the problem I am facing is how to filter data and make an almost similar array with the same data.
e.g.:-
0: [amount: 500, date: '2021-12-07'],
1: [amount: 1000, date: '2021-12-07']
The problem I am facing is I don't know the approach how to get the data like amount = sellAmount.USD and date = date.date
I thought of trying the for...of But I don't think it will be a good hit.
You can do this with Array.map
const arr = [{
buyAmount: 16.664328043964396,
buyAmountInUsd: 16.685266204095775,
date: {date: '2021-12-07'},
sellAmount: {USD: 500, INR: 35000}
},{
buyAmount: 1004.7015442959262,
buyAmountInUsd: 1005.9639175379324,
date: {date: '2021-12-07'},
sellAmount: {USD: 1000, INR: 79000}
}]
console.log(
arr.map(initialValue => {
return {
amount: initialValue.sellAmount.USD,
date: initialValue.date.date
}
})
)
The better idea would be the having an array of objects rather than array of array elements
let result = yourArray.map(each => ({ amount: each.sellAmount.USD, date: each.date.date }))
This is not related to ReactJS - just native Javascript object handling.
If your original data has the shape of an Object you can convert it into an Array like so:
const objData = { 0: {...}, 1: {...} };
const arrData = Object.entries(objData); // [[0, {...}], [1, {...}]];
From there, you can filter/map/sort your array with native array methods:
const reshapedArray = arrData.map(([key, value]) => {
return {
amount: value.sellAmount.USD,
date: value.date.date,
};
});
Then sort:
const sortedArray = reshapedArray.sort((prev, next) => {
return prev.amount - next.amount; // sort by amount ascending
});
You can of could chain these array functions and shorten the syntax a bit:
Object.entries(objData)
.map(([_, { sellAmount, date: { date } }]) => ({ amount: sellAmount.USD, date }))
.sort((a, b) => a.amount - b.amount);
I have an array of objects that contain a date and an amount (among other things).
There is an object for each date with a specific amount, but there can also be multiple objects for the same date containing different amounts.
I'd like to consolidate the objects so I only have one object in the array for each date ... and have the amount corresponding to that date be the total sum of all previous amounts in those objects.
Examples will probably help here:
What my array looks like now:
[
{
date: "2019-1-1", // this is a dupe date
amount: 20,
...
},
{
date: "2019-1-1", // this is a dupe date
amount: 40,
...
},
{
date: "2019-1-2",
amount: 40,
...
},
{
date: "2019-1-3",
amount: 40,
...
}
]
What I would like my array to look like:
[
{
date: "2019-1-1", // this is now a unique date
amount: 60, // and the amount was totaled
...
},
{
date: "2019-1-2",
amount: 40,
...
},
{
date: "2019-1-3",
amount: 40,
...
}
]
Use .reduce to reduce an array into an object (or into anything else) by iterating over its properties. You just need to test to see if an object with a matching date already exists in the accumulator first:
const input = [
{
date: "2019-1-1", // this is a dupe date
amount: 20,
foo: 'bar',
},
{
date: "2019-1-1", // this is a dupe date
amount: 40,
foo: 'bar',
},
{
date: "2019-1-2",
amount: 40,
foo: 'bar',
},
{
date: "2019-1-3",
amount: 40,
foo: 'bar',
}
];
const output = input.reduce((accum, item) => {
const { date, amount } = item;
const foundObj = accum.find(({ date: findDate }) => findDate === date);
if (foundObj) {
foundObj.amount += amount;
return accum;
}
accum.push(item);
return accum;
}, []);
console.log(output);
You may do as follows;
var data = [ { date: "2019-1-1", // this is a dupe date
amount: 20},
{ date: "2019-1-1", // this is a dupe date
amount: 40},
{ date: "2019-1-2",
amount: 40},
{ date: "2019-1-3",
amount: 40}
],
result = Object.values(data.reduce((r,d) => r[d.date] ? (r[d.date].amount += d.amount, r)
: (r[d.date] = d, r), {}));
console.log(result);
Regarding a comment i guess i have to explain this a little for those who may not be familiar with some ES6 functionalities.
Object.values() is a Object method which returns all own property values in an array.
So we are reducing our objects into an hash object of which we collect the properties by Object.values() later. While reducing we check if the currently examined object's date value exists as key in our map. If not we create that key and insert the examined object at that key position, if yes then we increment the previously inserted objects amount property by the value of currently examined objects amount value.
If you don't want to mutate the original data then please change r[d.date] = d into r[d.date] = Object.assign({},d).
The way I would do it is to create an object with the dates as the key, then you can iterate over the array and create a new date property if it doesn't exist or increase the amount if it does, then convert it back into an array:
const items = data.reduce((acc, curr) => {
if (!acc[curr.date]) { // basically creating a property with the date as the key and the value is the current object
acc[curr.date] = { ...curr };
} else { // if it exists already, then just increment the amount
acc[curr.date].amount += curr.amount;
}
return acc;
}, {});
const newArray = Object.values(items); // grab all the values from the object above
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);