Цель состоит в том, чтобы отобразить обзор последних действий.
Как пример: хотелось бы, чтобы отображались посты, комментарии, пользователи.
Пост, комментарий и пользовательский объект живут в соответствующих им массивах. Все объекты имеют метку времени (ниже createdAt), а также ключи, которых нет у объектов из разных массивов. Недавние действия должны быть отсортированы по отметке времени.
(Ultimately it should be sortable by different values, but first I would like to get a better general understanding behind merging and sorting arrays / objects and not making it to complicated)
Я подумал о том, чтобы каким-то образом объединить массивы во что-то вроде массива действий, затем отсортировать его и зациклиться на нем и условно вывести объект с его ключами в зависимости от того, какой это объект?
Если кто-то готов разобраться с этим, приведя пример, это сделает мой день. Лучшее, что я мог себе представить, это стройный REPL, решающий этот сценарий. В любом случае, я благодарен за каждую подсказку. Вероятно, уже есть хорошие примеры и ресурсы для этого (я думаю, распространенного) варианта использования, который я не нашел. Если бы кто-то мог сослаться на них, это также было бы превосходно.
Пример, который я собираюсь использовать, чтобы получить это концептуальное понимание:
const users = [
{ id: 'a', name: 'michael', createdAt: 1 },
{ id: 'b', name: 'john', createdAt: 2 },
{ id: 'c', name: 'caren', createdAt: 3 }
]
const posts = [
{ id: 'd', topic: 'food', content: 'nonomnom' createdAt: 4 },
{ id: 'e', name: 'drink', content: 'water is the best' createdAt: 5 },
{ id: 'f', name: 'sleep', content: 'i miss it' createdAt: 6 }
]
const comments = [
{ id: 'g', parent: 'd', content: 'sounds yummy' createdAt: 7 },
{ id: 'h', parent: 'e', content: 'pure life' createdAt: 8 },
{ id: 'i', parent: 'f', content: 'me too' createdAt: 9 }
]
Редактировать: это был бы немного лучший пример с более описательными ключами id, такими как userId, и когда объект сообщения и комментария содержит идентификатор пользователя. Тем не менее, приведенные ниже ответы делают его очень понятным и применимым для случаев использования в «реальном мире».
Мне действительно нужно получить некоторую репутацию, чтобы я мог голосовать за ваши ответы. Спасибо за все ваши советы, дорогие земляки!



Об этом интересно думать, и здорово, что вы продумываете архитектуру ленты активности.
Я бы сказал, что вы на правильном пути с тем, как вы думаете о подходе к этому.
Подумай о:
У вас есть 3 разных типы данных, и у вас есть общая лента активности, которую вы хотите создать. У каждого типа есть createdAt общего.
Есть несколько способов сделать это:
const activities = [...users, ...posts, ...comments];
activities.sort((a,b) => b.createdAt - a.createdAt); // Sort whichever way you want
Сложность здесь заключается в том, что при выводе вам понадобится способ указать, к какому типу объекта относится каждый элемент массива. Для пользователей вы можете искать ключ name, для сообщений вы можете искать ключ topic, для комментариев вы можете искать ключи parent/content для подтверждения типа объекта, но это немного хрупкий подход.
Давайте попробуем посмотреть, сможем ли мы сделать лучше.
const activities = [
...users.map((u) => ({...u, type: 'user'})),
...posts.map((u) => ({...u, type: 'post'})),
...comments.map((u) => ({...u, type: 'comment'}))
];
Теперь вы можете легко сказать, что любой данный элемент во всем массиве действий основан на его поле type.
В качестве бонуса это поле type также позволяет вам легко добавить функцию для фильтрации ленты активности только по определенным типам! И это также значительно упрощает добавление новых видов деятельности в будущем.
Вот машинописная площадка, показывающий это и регистрирующий вывод.
В качестве бонуса за безопасность типов вы можете добавлять типы в машинописный текст, чтобы усилить ожидаемые типы данных:
например.
type User = {
type: 'user';
name: string;
} & Common;
type Post = {
type: 'post';
topic: string;
content: string;
} & Common;
type UserComment = {
type: 'comment';
parent: string;
content: string;
} & Common;
type Activity = User | Post | UserComment;
Типы могут быть улучшены, чтобы включать type в качестве литералов, что позволяет выполнять сужение типа с использованием этого свойства (Детская площадка).
ах, упс, забыл включить поле type! Спасибо ХБ. @Truiiya, если вы нашли ответ полезным / в конечном итоге применили его, рассмотрите возможность пометить его как принятый!
Да спасибо. Как только я сяду за эту часть проекта на этой неделе, я попробую их, приложу все усилия, чтобы действительно понять их, и отмечу тот, который я решил использовать, как принятый. Также проголосуйте за все эти хорошие ответы, как только я смогу. Я узнал, что благодарить в комментариях - это не то, что нужно делать, а скорее голосовать и принимать ответы.
@Truiiya только что увидела, что вы отметили это, удачи в вашем проекте!
Вот подход, использующий простые «вспомогательные классы», чтобы можно было различать разные объекты при отображении РЕПЛ
<script>
class User {
constructor(obj){
Object.assign(this, obj)
}
}
class Post {
constructor(obj){
Object.assign(this, obj)
}
}
class Comment {
constructor(obj){
Object.assign(this, obj)
}
}
const users = [
{ id: 'a', name: 'michael', createdAt: 1652012110220 },
{ id: 'b', name: 'john', createdAt: 1652006110121 },
{ id: 'c', name: 'caren', createdAt: 1652018110220 }
].map(user => new User(user))
const posts = [
{ id: 'd', topic: 'food', content: 'nonomnom', createdAt: 1652016900220 },
{ id: 'e', topic: 'drink', content: 'water is the best', createdAt: 1652016910220 },
{ id: 'f', topic: 'sleep', content: 'i miss it', createdAt: 1652016960220 }
].map(post => new Post(post))
const comments = [
{ id: 'g', parent: 'd', content: 'sounds yummy', createdAt: 1652116910220 },
{ id: 'h', parent: 'e', content: 'pure life', createdAt: 1652016913220 },
{ id: 'i', parent: 'f', content: 'me too', createdAt: 1652016510220 }
].map(comment => new Comment(comment))
const recentActivities = users.concat(posts).concat(comments).sort((a,b) => b.createdAt - a.createdAt)
</script>
<ul>
{#each recentActivities as activity}
<li>
{new Date(activity.createdAt).toLocaleString()} -
{#if activity instanceof User}
User - {activity.name}
{:else if activity instanceof Post}
Post - {activity.topic}
{:else if activity instanceof Comment}
Comment - {activity.content}
{/if}
</li>
{/each}
</ul>
Я бы не рекомендовал полагаться на constructor.name, потому что минификаторы могут исказить их при сборке. Если вы все равно используете классы, вы можете просто использовать, например. activity instanceof User вместо этого.
@Х.Б. Спасибо за совет! Изменил это...
Чтобы расширить другие ответы, в конечном итоге вы захотите показать каждый элемент также по-разному, хотя вы можете сделать это с помощью блока if, тестирующего type, который был добавлен к объекту, это не очень масштабируемо, поскольку новый тип блока требуется как минимум два изменения: одно для добавления типа в массив activities и одно для добавления этого нового типа в блоки if.
Вместо этого, если мы изменим наш массив действий следующим образом:
const activities = [
...users.map((u) => ({...u, component: UserCompomnent})),
...posts.map((u) => ({...u, component: PostComponent})),
...comments.map((u) => ({...u, component: CommentComponent}))
];
где UserComponent, PostComponent и CommentComponent — различные способы представления этих данных.
Затем, когда вы перебираете свои данные для их отображения, мы можем использовать svelte:component и использовать то, что мы уже определили, какой компонент должен отображаться:
{#each acitivities as activity}
<svelte:component this = {activity.component} {...activity} />
{/each}
Это потрясающе! Большое спасибо! Я понял, что пример, который я привел, немного ошибочен, но, насколько я могу думать с точки зрения моего вопроса, этот ответ помогает понять все, что я искал! Спасибо еще раз