I have an object array with a structure similar to this:
export interface obj {
id: number,
date: string,
source: string,
}
const obj: obj[] | undefined = [
{ id: 1, date: "2021-01-17", source: "data" },
{ id: 2, date: "2021-11-23", source: "data" },
{ id: 3, date: "2020-05-03", source: "draft" },
{ id: 4, date: "2022-09-08", source: "draft" },
{ id: 5, date: "2021-12-04", source: "data" },
{ id: 6, date: "2021-09-08", source: "empty" },
];
const [objectData, setObjectData] = useState<obj[]>();
I'm trying to return and render the first occuranses of each source type, sorted by the nearest date. So in the example above I'd like to return the object with id: 5 for "data", id: 4 for "draft" and id: 6 for "empty".
This is what I got so far:
Sorting object by dates descending in useEffect and store variable in useState
const sortByDate = obj?.slice().sort((a, b) => {
return b.date.valueOf() - a.date.valueOf();
})
And then trying to map out my component like so:
{objectData?.map((m) =>
m.source === "draft" ? (
<Data id={m.id} date={m.date} source={m.source} />
) : m.applicationType === "data" ? (
<Data id={m.id} date={m.date} source={m.source} />
) : m.applicationType === "empty" ? (
<Data id={m.id} date={m.date} source={m.source} />
)
)}
But this will ofcourse render out every object..How can I render out only the one instance of each object source type that i want?
CodePudding user response:
Given your approach, I would a) Sort the items in the list in a descending manner. b) Create a list of possible unique source names c) Render the unqiue source names and get the first item matching the source name in my descending ordered array.
You can get rid of b) if you want to define your list of source names statically. If that is the case, you need to handle if there is no item found with the source name that is selectecd.
const items = [
{ id: 1, date: "2021-01-17", source: "data" },
{ id: 2, date: "2021-11-23", source: "data" },
{ id: 3, date: "2020-05-03", source: "draft" },
{ id: 4, date: "2022-09-08", source: "draft" },
{ id: 5, date: "2021-12-04", source: "data" },
{ id: 6, date: "2021-09-08", source: "empty" },
];
// a) Sort the array with date object (descending)
const sortedArray = items.sort((a, b) => {
return new Date(b.date).valueOf() - new Date(a.date).valueOf();
});
// b) This will let you get all possible sources dynamically and create an array of unique entries
const uniqueSources = [...new Set(sortedArray.map(item => item.source))];
// c) Map over (existing) unqiue entries in the sorted list and get the first one found
uniqueSources.map((source) => {
const firstObject = sortedArray.find((entry) => entry.source === source);
console.log('source:', source, firstObject);
});
CodePudding user response:
I think you probably do (or should) know the possible values of source
, and in fact those should be the type for source
, ex:
export interface obj {
id: number,
date: string,
source: 'data' | 'draft' | 'empty',
}
Then in your useEffect
you have the internal variable sortedByMostRecent
which is your array of data sorted by the date value.
With that you can use a constant array (defined at the top of your file, outside the component definition around the interface definition) of the source
options:
const availableSources: interface['source'][] = ['data', 'draft', 'empty'];
To find the first object in the sorted array matching each available (possible) source. Something like:
const topSourceItems = [];
for (source of availableSources) {
const topSource = sortedByMostRecent.find(item => item.source == source);
if (topSource) {
topSourceItems.push(topSource);
}
}
topSourceItems
would then be stored in state and rendered with a regular iteration in the markup.
Notes:
Your .slice()
isn't doing anything. Also calling an array
"obj" is probably not the best decision, but I understand this is likely just dummy code for the example.