Как получить глубоко вложенную структуру объекта из массива значений с помощью Javascript
У меня есть JSON, представляющий некоторый контент в текстовом редакторе. Вот образец:
{ "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "text", "marks": [ { "type": "bold" } ], "text": "Testing" }, { "type": "text", "text": " " }, { "type": "text", "marks": [ { "type": "strike" } ], "text": "Testing " }, { "type": "text", "marks": [ { "type": "strike" }, { "type": "underline" } ], "text": "Testing" }, { "type": "text", "marks": [ { "type": "strike" } ], "text": " Testing" } ] }, { "type": "paragraph" }, { "type": "paragraph", "content": [ { "type": "text", "marks": [ { "type": "bold" } ], "text": "Testing " }, { "type": "text", "marks": [ { "type": "bold" }, { "type": "italic" }, { "type": "strike" } ], "text": "Testing" }, { "type": "text", "marks": [ { "type": "bold" } ], "text": " Testing" } ] }, { "type": "paragraph" } ] }
Это хорошо, потому что я могу безопасно превратить его в html, однако вместо вложенных значений для стилей он представляет этот стиль в marks
подмассив. Я бы предпочел хранить данные, уже вложенные в их естественную иерархию.
Так что вместо:
{
"type":"text",
"marks": [{ "type": "bold" }, { "type": "italic" }],
"text": "Testing"
}
Я хотел бы иметь что-то, что более точно представляет результирующий HTML:
{
"type" : "bold",
"content" : [{
"type" : "italic",
"content":[{
"type" : "text",
"text" : "Testing"
}]
}]
}
Я пробовал различные способы синтаксического анализа json и его сохранения, как показано ниже, но я думаю, что мне не хватает чего-то фундаментального о том, как javascript работает с объектами, потому что изменения не вносятся.
Я выполнил различные итерации функции ниже, для каждого marks
массив в файле json.
item = {type:type, text:text}
marks.forEach((mark)=>{
let newObj = {content:[], type:mark.type}
item = newObj.content.push(item)
});
Дополнительная справочная информация, это json, сгенерированный редактором tiptap wysiwyg, который основан на prosemirror. Я не могу использовать обычный экспорт html, потому что я делаю особые вещи с данными. Любая помощь будет принята с благодарностью.
РЕДАКТИРОВАТЬ: Дакр Денни придумал ответ, используя reduceRight()
который ответил на первую часть проблемы. Однако приведенная ниже функция по-прежнему возвращает исходный объект без изменений.
const content = editorContent.content
function parseContent (arr) {
arr.forEach((item)=>{
// Check if item contains another array of items, and then iterate
over that one too.
if(item.content){
parseContent(item.content)
}
if(item.marks){
//Extract values from item
// THis is the correct function
const processContent = ({ marks, text }) =>
marks.reduceRight((acc, mark) => ({ type: mark.type, content: [acc]
}), {
type: "text",
text: text
});
item = processContent({ text:item.text, marks:item.marks })
}
})
}
parseContent(content)
return content
//
1 ответ
Одна из возможностей - использовать reduceRight
"уменьшить" marks
подмассив вашего входного содержимого в один объект с необходимой "вложенной структурой".
Идея состоит в том, чтобы начать сокращение с { type : "text", text : content.text }
объект (который будет вложен), а затем постепенно "обернуть" этот объект объектами, которые содержат данные "типа".
Чтобы добиться правильного порядка "упаковки" для объектов с метаданными типа, marks
массив нужно уменьшить в обратном порядке. Вот почему reduceRight
используется (а не обычный reduce
).
Вот пример фрагмента, демонстрирующий эту идею в действии:
/* Helper function processes the content object, returns the required
nested structure */
const processContent = ({ marks, text }) =>
marks.reduceRight((acc, mark) => ({ type: mark.type, content: [acc]
}), {
type: "text",
text: text
});
console.log(processContent({
"type": "text",
"marks": [{
"type": "bold"
}, {
"type": "italic"
}],
"text": "Testing"
}))
/*
Result:
{
"type" : "bold",
"content" : [{
"type" : "italic",
"content":[{
"type" : "text",
"text" : "Testing"
}]
}]
}
*/
Надеюсь, это поможет!