
本文介绍如何将有序的聊天消息数组,按 userId 的连续变化切分为多个子数组,实现“发言块”分组,适用于前端消息气泡渲染等场景。
本文介绍如何将有序的聊天消息数组,按 `userid` 的连续变化切分为多个子数组,实现“发言块”分组,适用于前端消息气泡渲染等场景。
在构建聊天界面时,单纯按用户 ID 分组(如 groupBy('userId'))无法满足视觉呈现需求——因为同一用户可能多次交替发言,而 UI 上需将连续发送的多条消息合并为一个“发言块”(例如左侧气泡组或右侧气泡组)。这就要求:不按全局用户聚合,而是按时间顺序中 userId 的连续段进行切分。
核心思路是:先确保消息数组已按 timestamp 升序排序(服务端或客户端调用 .sort((a, b) => a.timestamp - b.timestamp)),再遍历数组,每当 userId 相较前一条发生变更时,就开启一个新的消息块;否则追加到当前块末尾。
以下为简洁、健壮的实现方案(使用 Array.reduce):
function groupMessagesByContinuousSender(messages) {
if (!Array.isArray(messages) || messages.length === 0) return [];
return messages.reduce((groups, current, index) => {
// 首条消息,或当前 sender 与前一条不同 → 新建发言块
if (index === 0 || current.userId !== messages[index - 1].userId) {
groups.push([current]);
} else {
// 连续同 sender → 追加至最后一组
groups[groups.length - 1].push(current);
}
return groups;
}, []);
}
// 示例使用
const messages = [
{ content: 'content 1', userId: '123', targetUserId: '345', timestamp: new Date('2023-11-06T19:57:42.124Z') },
{ content: 'content 2', userId: '123', targetUserId: '345', timestamp: new Date('2023-11-06T19:58:42.124Z') },
{ content: 'content 3', userId: '123', targetUserId: '345', timestamp: new Date('2023-11-06T19:59:42.124Z') },
{ content: 'content 4', userId: '345', targetUserId: '123', timestamp: new Date('2023-11-06T20:00:42.124Z') },
{ content: 'content 5', userId: '345', targetUserId: '123', timestamp: new Date('2023-11-06T20:01:42.124Z') },
{ content: 'content 6', userId: '123', targetUserId: '345', timestamp: new Date('2023-11-06T20:02:42.124Z') },
{ content: 'content 7', userId: '123', targetUserId: '345', timestamp: new Date('2023-11-06T20:03:42.124Z') }
];
const grouped = groupMessagesByContinuousSender(messages);
console.log(grouped);
// 输出 3 个子数组,分别对应 user '123' → '345' → '123' 的连续发言块
✅ 关键注意事项:
- ✅ 务必预先排序:该算法严格依赖
messages已按timestamp升序排列,否则分组逻辑失效; - ✅ 比较的是相邻项:仅对比
current.userId与messages[index - 1].userId,而非上一块的 sender,确保“连续性”语义准确; - ⚠️ 避免副作用:
reduce中未修改原数组,返回全新嵌套结构,符合函数式编程规范; - ? 扩展建议:如需支持双向会话标识(如
conversationId),可将判断条件升级为(index === 0 || current.conversationId !== prev.conversationId || current.userId !== prev.userId)。
此方法轻量、无依赖、兼容性好(ES5+),可直接集成于 React/Vue 等框架的消息列表渲染逻辑中,为气泡分组、时间分隔线、发送者头像复用等提供结构化数据基础。









