I have the following JSON array
{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:avatar_de332fbebe0906c451c8859294d1553d"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~malhajj?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4"}}]},{"type":"text","text":" Mohamad AL HAJJ "},{"type":"text","text":"mentioned","marks":[{"type":"strong"}]},{"type":"text","text":" you on a page"}]},{"type":"paragraph","content":[{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:mention-icon","alt":"mention icon","title":"mention icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"Bassem 1-1 2022-07-28","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"···"}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]}]}]},{"type":"paragraph","content":[{"type":"text","text":"Release 3.0.7 is ready"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[]}]}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" Research about supporting React in windows app"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" "},{"type":"hardBreak"},{"type":"text","text":" Test server changes done by Ahmad"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"}]},{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_view-page-email-adg-footer-item_icon","alt":"View page Icon","title":"View page Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"View page","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_add-comment-to-content-email-adg-footer-item_icon","alt":"Add comment Icon","title":"Add comment Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" "},{"type":"text","text":"Add comment","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-like_view-email-adg-content-item_icon","alt":"Like Icon","title":"Like Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":"Like","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"Manage notifications","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/users/editmyemailsettings.action?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=manage"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:footer-desktop-logo","alt":"Confluence logo big","title":"Confluence logo big"}},{"type":"text","text":" This message was sent by Atlassian Confluence 7.18.1"},{"type":"hardBreak"},{"type":"image","attrs":{"src":"cid:footer-mobile-logo","alt":"","title":""}}]}]}
what I am trying to do is creating a recursive function that locate all the elements with type image and try to move it from inside it's parent and push it as a root element
but I am not able to push it right after or before it's original parent.
This is what I did
static recur = (arr, AttachmentsArr, madfdoc, htmlbody) => {
if (arr && arr.length > 0) {
arr.forEach(item => {
try {
switch (item.type) {
case "image":
this.handleImages(arr, madfdoc, item, htmlbody, AttachmentsArr)
break;
case "paragraph":
this.handleParaphs(item, htmlbody, AttachmentsArr)
break;
default:
break;
}
if (item.content) {
this.recur(item.content, AttachmentsArr, madfdoc, htmlbody);
}
}
catch (ex) {
console.log(ex)
}
});
}
};
static handleImages(arr, madfdoc, item, htmlbody, AttachmentsArr) {
var imghtmlobj = htmlbody.querySelector(`img[src*='${item.attrs.src}']`);
let src = item.attrs.src.replace("cid:", "");
// find the image bloburl to change it
let fnd = AttachmentsArr && AttachmentsArr.length ? AttachmentsArr.find(obj => {
return obj.ContentId ? obj.ContentId === src : undefined;
}) : undefined
var imgw = undefined;
var imgh = undefined;
if (imghtmlobj) {
let img = new Image();
img.onload = function () {
imgw = this.width;
imgh = this.height;
delete item["attrs"]
delete item["marks"]
item.type = "mediaSingle";
item.content = [];
item.content.push({
type: "media",
attrs: {
type: "external",
url: fnd ? fnd.preview : src,
width: imgw,
height: imgh
}
})
const indexOfObject = arr.findIndex(object => {
return object.type === "mediaSingle";
});
**arr.splice(indexOfObject, 1); // here is where i am deleting it from inside it's parent and push it to the root inside `madfdoc`
madfdoc.content.push(item);**
}
img.src = fnd ? fnd.preview : src;
}
}
The expected Json would be something like this
{
"type": "doc",
"version": 1,
"content": [
{
"type": "image",
"attrs": {
"src": "cid:avatar_de332fbebe0906c451c8859294d1553d"
},
"marks": [
{
"type": "link",
"attrs": {
"href": "https://wiki.infosysta.com/display/~malhajj?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4"
}
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": " test"
},
{
"type": "text",
"text": "mentioned",
"marks": [
{
"type": "strong"
}
]
},
{
"type": "text",
"text": " you on a page"
}
]
}
]
}
If I understood your requirement correctly, this should do it:
class ImagePopper {
#data;
#collector = [];
constructor(data) {
if (typeof data === "object") {
this.#data = structuredClone(data);
this.parseObject(this.#data, []);
this.#collector.reverse().forEach(({ entry, path }) => {
this.#data[path[0]].splice(path[1], 0, entry);
});
}
return this.#data || data;
}
parseArray = (arr, path) => {
_.set(
this.#data,
path,
arr.reduce((acc, entry, index) => {
if (typeof entry === "object") {
this.parseObject(entry, [...path, index]);
}
if (entry?.type === "image") {
this.#collector.push({ entry, path });
} else {
acc.push(entry);
}
return acc;
}, [])
);
};
parseObject = (obj, path) =>
Object.entries(obj).forEach(
([key, value]) =>
Array.isArray(value) && this.parseArray(value, [...path, key])
);
}
const json = {"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:avatar_de332fbebe0906c451c8859294d1553d"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~malhajj?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4"}}]},{"type":"text","text":" Mohamad AL HAJJ "},{"type":"text","text":"mentioned","marks":[{"type":"strong"}]},{"type":"text","text":" you on a page"}]},{"type":"paragraph","content":[{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:mention-icon","alt":"mention icon","title":"mention icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"Bassem 1-1 2022-07-28","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"···"}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]}]}]},{"type":"paragraph","content":[{"type":"text","text":"Release 3.0.7 is ready"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[]}]}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" Research about supporting React in windows app"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" "},{"type":"hardBreak"},{"type":"text","text":" Test server changes done by Ahmad"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"}]},{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_view-page-email-adg-footer-item_icon","alt":"View page Icon","title":"View page Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"View page","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_add-comment-to-content-email-adg-footer-item_icon","alt":"Add comment Icon","title":"Add comment Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" "},{"type":"text","text":"Add comment","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-like_view-email-adg-content-item_icon","alt":"Like Icon","title":"Like Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":"Like","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"Manage notifications","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/users/editmyemailsettings.action?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=manage"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:footer-desktop-logo","alt":"Confluence logo big","title":"Confluence logo big"}},{"type":"text","text":" This message was sent by Atlassian Confluence 7.18.1"},{"type":"hardBreak"},{"type":"image","attrs":{"src":"cid:footer-mobile-logo","alt":"","title":""}}]}]}
const result = new ImagePopper(json);
// console.log({ before: json, after: result });
before.innerHTML = JSON.stringify(json, null, 2)
after.innerHTML = JSON.stringify(result, null, 2)
#media (min-width: 800px) {
.side-by-side {
display: flex;
}
.side-by-side > div {
width: 50%;
}
.scroller {
overflow: auto;
height: calc(100vh - 84px);
}
}
pre {
padding: .5rem;
background-color: #f5f5f5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<div class="side-by-side">
<div>
<h2>Before</h2>
<div class="scroller">
<pre id="before"></pre>
</div>
</div>
<div>
<h2>After</h2>
<div class="scroller">
<pre id="after"></pre>
</div>
</div>
</div>
I'm traversing the data recursively and collect all images, storing their current path. At each level, I'm deleting the images from their parent (by omission). I used lodash's set for this (it allows passing the path as array, which was quite handy here). 1, 2
Then I'm injecting each image at top level before its root parent, in reverse order. This way I don't need to always inflate the splice index by the number of previously processed images.
Decided to wrap it as a class to keep it from interacting with the outer scope, for cleanness, readability and reusability.
That's about it.
Notes:
1 - If you don't want to use lodash's set, consider using this implementation.
2 - in your project, you shouldn't import all of lodash, as I'm doing here. Only import set:
import { set } from 'lodash-es'
// replace `_.set(` in the class with just `set(`
Focussing on your description, I would build a new json object and add the properties in the order I want them to appear.
Assuming your json data is in the data variable, the following would recursively parse the root content, remove any images it finds and later insert them after the root content element it found them in.
const data = `{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:avatar_de332fbebe0906c451c8859294d1553d"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~malhajj?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4"}}]},{"type":"text","text":" Mohamad AL HAJJ "},{"type":"text","text":"mentioned","marks":[{"type":"strong"}]},{"type":"text","text":" you on a page"}]},{"type":"paragraph","content":[{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:mention-icon","alt":"mention icon","title":"mention icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"Bassem 1-1 2022-07-28","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"···"}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]}]}]},{"type":"paragraph","content":[{"type":"text","text":"Release 3.0.7 is ready"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[]}]}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" Research about supporting React in windows app"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"},{"type":"text","text":" "},{"type":"hardBreak"},{"type":"text","text":" Test server changes done by Ahmad"}]},{"type":"paragraph","content":[{"type":"text","text":"#"},{"type":"text","text":"Bassem Alameddine","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/~balameddine"}}]},{"type":"text","text":" "}]},{"type":"paragraph","content":[{"type":"hardBreak"}]},{"type":"paragraph","content":[{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_view-page-email-adg-footer-item_icon","alt":"View page Icon","title":"View page Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" "},{"type":"text","text":"View page","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=view"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-email-resources_add-comment-to-content-email-adg-footer-item_icon","alt":"Add comment Icon","title":"Add comment Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" "},{"type":"text","text":"Add comment","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/display/MDT/Bassem+1-1+2022-07-28?showComments=true&showCommentArea=true&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=comment#addcomment"}}]},{"type":"text","text":" • "},{"type":"image","attrs":{"src":"cid:com.atlassian.confluence.plugins.confluence-like_view-email-adg-content-item_icon","alt":"Like Icon","title":"Like Icon"},"marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":"Like","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/plugins/likes/like.action?contentId=26543947&src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=like&jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4c3JmOjMwYmFmMWJhN2NjZGRkZDMwMTdmY2ExMzAwN2UwMGM0IiwicXNoIjoiODEwNWNmMmVlZjcwMGQ1MjVmYjUyODkzMjFmYTMzZGQyNDNmNDNmM2M2MWY1YmU5NzU3YjhjMWVmZmIxZTk1MSIsImlzcyI6ImNvbmZsdWVuY2Vfbm90aWZpY2F0aW9uc0JFOFUtTDNMWi1USzFPLVBGM00iLCJleHAiOjE2NTk3NzM1NTYsImlhdCI6MTY1OTE2ODc1Nn0.R4yPR6SKdUg5mc8fwgdGUa7BeWOdN_2LXDZbAKC5A-o"}}]},{"type":"text","text":" "},{"type":"text","text":" ","marks":[{"type":"textColor","attrs":{"color":"#ffffff"}}]}]},{"type":"paragraph","content":[{"type":"text","text":"Manage notifications","marks":[{"type":"link","attrs":{"href":"https://wiki.infosysta.com/users/editmyemailsettings.action?src=mail&src.mail.product=confluence-server&src.mail.timestamp=1659168756680&src.mail.notification=com.atlassian.confluence.plugins.confluence-mentions-plugin%3Amention-created-notification&src.mail.recipient=30baf1ba7ccdddd3017fca13007e00c4&src.mail.action=manage"}}]},{"type":"text","text":" "},{"type":"image","attrs":{"src":"cid:footer-desktop-logo","alt":"Confluence logo big","title":"Confluence logo big"}},{"type":"text","text":" This message was sent by Atlassian Confluence 7.18.1"},{"type":"hardBreak"},{"type":"image","attrs":{"src":"cid:footer-mobile-logo","alt":"","title":""}}]}]}`;
const findAndRemoveImagesRecursive = (element) => {
let images = [];
if (Array.isArray(element.content)) {
element.content.forEach((c, i) => {
if (c.type === 'image') {
images.push({
...c
})
delete element.content[i];
}
else {
images = images.concat(findAndRemoveImagesRecursive(c));
}
})
element.content = element.content.filter(x=>x);
}
return images;
}
const newJson = {...data, content: []};
data.content.forEach(e => {
const images = findAndRemoveImagesRecursive(e);
newJson.content = newJson.content.concat(images);
newJson.content.push(e);
});
I have a javascript structure like below (nested arrays of objects)
var categoryGroups = [
{
Id: 1, Categories: [
{ Id: 1 },
{ Id: 2 },
]
},
{
Id: 2, Categories: [
{ Id: 100 },
{ Id: 200 },
]
}
]
I want to find a child Category object matching an Id, assuming the Category Id's are all unique.
I've got this below, but was wondering if there is a more concise way of doing it:
var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
categoryGroup = categoryGroups[i];
for (j = 0; j < categoryGroup.Categories.length; j++) {
category = categoryGroup.Categories[j];
if (category.Id === id) {
found = true;
break;
}
}
if (found) break;
}
Using flatMap in ES2019
const category = categoryGroups.flatMap(cg => cg.Categories).find(c => c.Id === categoryId);
Caveat: This uses a couple of Array.prototype functions that were only added in ECMAScript 5 and thus will not work with older browsers unless you polyfill them.
You can loop over all first-level objects in your array, and then filter the categories based on your condition and collect all matches in an array. Your final result will be the first element in the array of matches (no match found if array is empty).
var matches = [];
var needle = 100; // what to look for
arr.forEach(function(e) {
matches = matches.concat(e.Categories.filter(function(c) {
return (c.Id === needle);
}));
});
console.log(matches[0] || "Not found");
JSFiddle: http://jsfiddle.net/b7ktf/1/
References:
Array.prototype.forEach
Array.prototype.concat
Array.prototype.filter
Using only Array.prototype.filter():
If you are sure that the id you are looking for exists, you can do:
var id = 200; // surely it exists
var category = arr.filter(g => g.Categories.filter(c => c.Id === id)[0])[0].Categories.filter(c => c.Id === id)[0];
If you are not sure that it exists:
var id = 201; // maybe it doesn't exist
var categoryGroup = arr.filter(e => e.Categories.filter(c => c.Id === id)[0])[0];
var category = categoryGroup ? categoryGroup.Categories.filter(c => c.Id === id)[0] : null;
jsfiddle
Using reduce and recursion :
function nestedSearch(value) {
return categoryGroups.reduce(function f(acc, val) {
return (val.Id === value) ? val :
(val.Categories && val.Categories.length) ? val.Categories.reduce(f, acc) : acc;
});
}
> try on JSFiddle
check the code in the fiddle
var categoryGroups = [
{
Id: 1, Categories: [
{ Id: 1 },
{ Id: 2 },
]
},
{
Id: 2, Categories: [
{ Id: 100 },
{ Id: 200 },
]
}
]
var id = 100;
var x = 'not found';
var category, categoryGroup, found = false;
for (i = 0; i < categoryGroups.length ; i++) {
categoryGroup = categoryGroups[i];
for (j = 0; j < categoryGroup.Categories.length; j++) {
category = categoryGroup.Categories[j];
if (category.Id == id) {
var x = category.Id;
found = true;
break;
}
}
if (found) break;
}
alert(x);
The above code checks if id = 100 is found in the array. If found will alert the value else alerts that its not found. value '100' has been hardcoded for the sake of demo
You could wrap it inside a function to get rid of the awkward break; syntax and you can load each element into a variable inside the for(;;) construct to shave off a few lines.
function subCategoryExists(groups, id)
{
for (var i = 0, group; group = groups[i]; ++i) {
for (var k = 0, category; category = group.Categories[k]; ++k) {
if (category.Id == id) {
return true;
}
}
}
return false;
}
var found = subCategoryExists(categoryGroups, 100);
Easy way using lodash library of NodeJS (assuming you are using NodeJS):
const _ = require('lodash');
let category ;
let categoryGroup = _.find(categoryGroups, (element)=>{
category = _.find(element.Categories, {Id : 100});
return category;
});
console.log(categoryGroup); // The category group which has the sub category you are looking for
console.log(category); // The exact category you are looking for
If you want to actually return the inner category (instead of just checking for it's presence) you can use reduce:
return categoryGroups.reduce((prev, curr) => {
//for each group: if we already found the category, we return that. otherwise we try to find it within this group
return prev || curr.Categories.find(category => category.Id === id);
}, undefined);
This short-circuits on the inner categories, and touches each categoryGroup once. It could be modified to short-cicuit on the categoryGroups as well.
Here's a JS Fiddle demonstration.
You could use underscore:
var cat = _(categoryGroups).
chain().
pluck('Categories').
flatten().
findWhere({Id: 2}).
value();
What I'm doing here is that I'm extracting all Categories values in a single array and then grepping for the correct categories.
EDIT: sorry, didn't get your question right the first time. As the comments suggest, you might not want to use underscore just for that, but that's how I would do it :)
We are using object-scan for our data processing now. It's very powerful once you wrap your head around it. For your questions this would look like this:
// const objectScan = require('object-scan');
const lookup = (id, data) => objectScan(['Categories.Id'], {
useArraySelector: false,
abort: true,
rtn: 'parent',
filterFn: ({ value }) => value === id
})(data);
const categoryGroups = [{ Id: 1, Categories: [{ Id: 1 }, { Id: 2 }] }, { Id: 2, Categories: [{ Id: 100 }, { Id: 200 }] }];
console.log(lookup(1, categoryGroups));
// => { Id: 1 }
console.log(lookup(100, categoryGroups));
// => { Id: 100 }
console.log(lookup(999, categoryGroups));
// => undefined
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan