异常的数据分组/转换



我正在努力解决一个算法问题,如何转换或分组数据以获得指定的输出。

我的输入是一堆按给定顺序(从最新到最旧)的消息:

[
{
"id":5,
"created_at":"2021-01-01 00:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":true,
"meta_data":{

}
},
{
"id":4,
"created_at":"2021-01-01 01:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":true,
"meta_data":{

}
},
{
"id":3,
"created_at":"2021-01-01 03:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":false,
"meta_data":{

}
},
{
"id":2,
"created_at":"2021-01-01 04:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":false,
"meta_data":{

}
},
{
"id":1,
"created_at":"2021-01-01 05:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":true,
"meta_data":{

}
},
{
"id":0,
"created_at":"2021-01-01 06:00:00",
"message":"Lorem ipsum dolor sit amet...",
"is_author":false,
"meta_data":{

}
}
]

转换后所需的输出:

[
{
"is_author":true,
"messages":[
{
"id":5,
"created_at":"2021-01-01 00:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
},
{
"id":4,
"created_at":"2021-01-01 01:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
}
]
},
{
"is_author":false,
"messages":[
{
"id":3,
"created_at":"2021-01-01 03:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
},
{
"id":2,
"created_at":"2021-01-01 04:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
}
]
},
{
"is_author":true,
"messages":[
{
"id":1,
"created_at":"2021-01-01 05:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
}
]
},
{
"is_author":false,
"messages":[
{
"id":0,
"created_at":"2021-01-01 06:00:00",
"message":"Lorem ipsum dolor sit amet...",
"meta_data":{

}
}
]
}
]

如您所见,每次出现 is_author参数都会创建一个新组,该组收集来自此作者的消息?

JS或PHP中是否有任何有效的解决方案来转换此类数据?

抽象一点有很多话要说。 在这里,我们希望在两个连续元素之间发生更改时拆分数组。 这是基本的分组,如果我们这样想,我们可以将它们的集合分层到该分组之上的单个{is_author, messages}对象中。

因此,我们可能会编写一个泛型函数,每当上一个和当前项的某些函数返回true时,它就会将数据拆分为子数组。 然后我们的 main 函数会调用它,传递一个函数来测试is_author属性是否不同,然后在返回后重新格式化生成的组。 它可能看起来像这样:

const splitWhenever = (pred) => (xs) =>
xs .length == 0 ? [] : xs .slice (1) .reduce (
((xss, x, i) => pred (xs [i], x) 
? [...xss, [x]] 
: [...xss .slice (0, -1), [... xss [xss .length - 1], x]]
), [[xs [0]]]
)
const transform = (input) => splitWhenever ((x, y) => x.is_author != y.is_author) (input)
.map ((xs => ({
is_author: xs [0] .is_author, 
messages: xs .map (({is_author, ...rest}) => rest)
})))
const input = [{id: 5, created_at: "2021-01-01 00:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 4, created_at: "2021-01-01 01:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 3, created_at: "2021-01-01 03:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}, {id: 2, created_at: "2021-01-01 04:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}, {id: 1, created_at: "2021-01-01 05:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 0, created_at: "2021-01-01 06:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}]
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

这比 navnath 的答案更复杂。 但它建立在splitWherever之上,现在可以在这个程序和其他程序中重用。

在 navnath 的回答之后,OP 问道:"如果我处理了一组数据并且应用程序加载了新数据 [...] 是否可以将来自对象 ID 6 的消息加入到现有组? 我在那里评论了一个建议的更改,只需对 navanth 的代码进行微小的调整即可做到这一点。 但重读后,我认为这个新数据应该先于现有数据出现。(因为现有 id 是降序排序的,而新 id 大于这些。 这意味着我的建议可能行不通。

这里需要更多的工作,因为我们已经将分组与重新格式化分开。 这个版本仍然使用相同的泛型splitWhenever,将首先将现有结构展平为原始格式,在前面附加新数据,然后重新运行。 这听起来可能很浪费。 也许是吧。 更好的办法是简单地保留原始列表,在前面加上它,然后重新运行上面的方法。 但是由于我们的转换是可逆的,如果需要,这将起作用:

const splitWhenever = (fn) => (xs) =>
xs .length == 0 ? [] : xs .slice (1) .reduce (
((xss, x, i) => fn (xs [i], x) 
? [...xss, [x]] 
: [...xss .slice (0, -1), [... xss [xss .length - 1], x]]
), [[xs [0]]]
)
const transform = (input, old = []) => splitWhenever ((x, y) => x.is_author != y.is_author) ([
...input, 
...old.flatMap (({is_author, messages}) => messages .map (msg => ({...msg, is_author})))
]).map ((xs => ({
is_author: xs [0] .is_author, 
messages: xs .map (({is_author, ...rest}) => rest)
})))
const input = [{id: 5, created_at: "2021-01-01 00:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 4, created_at: "2021-01-01 01:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 3, created_at: "2021-01-01 03:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}, {id: 2, created_at: "2021-01-01 04:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}, {id: 1, created_at: "2021-01-01 05:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}, {id: 0, created_at: "2021-01-01 06:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}]
const result = transform (input)
console .log ('Original data')
console .log (result)
const additional = [{id: 7, created_at: "2020-12-31 22:00:00", message: "Lorem ipsum dolor sit amet...", is_author: false, meta_data: {}}, {id: 6, created_at: "2020-12-31 22:00:00", message: "Lorem ipsum dolor sit amet...", is_author: true, meta_data: {}}]
const result2 = transform (additional, result)
console .log ('With additional results')
console .log (result2)
.as-console-wrapper {max-height: 100% !important; top: 0}

const data=[{"id":5,"created_at":"2021-01-01 00:00:00","message":"Lorem ipsum dolor sit amet...","is_author":true,"meta_data":{}},{"id":4,"created_at":"2021-01-01 01:00:00","message":"Lorem ipsum dolor sit amet...","is_author":true,"meta_data":{}},{"id":3,"created_at":"2021-01-01 03:00:00","message":"Lorem ipsum dolor sit amet...","is_author":false,"meta_data":{}},{"id":2,"created_at":"2021-01-01 04:00:00","message":"Lorem ipsum dolor sit amet...","is_author":false,"meta_data":{}},{"id":1,"created_at":"2021-01-01 05:00:00","message":"Lorem ipsum dolor sit amet...","is_author":true,"meta_data":{}},{"id":0,"created_at":"2021-01-01 06:00:00","message":"Lorem ipsum dolor sit amet...","is_author":false,"meta_data":{}}];
const result = data.reduce((acc, item) => {
const { is_author, ...rest } = item;
// if is_author match with previous object
if(acc.length && acc[acc.length-1].is_author === is_author){
// copy previous messages, along with current item
acc[acc.length-1].messages = [...acc[acc.length-1].messages, rest ];
}else{
acc.push({ is_author, messages: [ rest ] });
}
return acc;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }

最新更新