Reputation: 105
In https://codepen.io/kurt_cagle/pen/xqoMBG I find a function walkData with the following statement:
var buf = Object.keys(data).map((key)=>`<details><summary id="${key}" ${Object.keys(data[key]).map((subkey)=>{return subkey != 'children'?`data-${subkey}="${data[key][subkey]}"`:' '}).join(' ')}><img class="icon" src="${me.imageBase}${data[key].icon?data[key].icon:data[key].children?'Folder.png':'Item.png'}"> </img>${data[key].label}</summary>
${data[key].children?me.walkData(data[key].children):""}</details>`);
As an old-school-non-functional-dinosaur, I find that an unholy mess of map and interpolation which is almost impossible to follow, debug, or modify. The codepen "Format Javascript" button helps, but not enough. (I would post that here but the formating defeats me)
Can this be reworked to use longhand loops, intermediate variables, and shorter lines that ideally do just one thing each.
As a side issue, there are four ` in that one line, can anyone explain to me how the js manages to parse that?
Upvotes: 1
Views: 80
Reputation: 105
This is what I have ended up with (some var names may be a bit cringeworthy).
const buf = []
for (const key in data) {
const dk = data[key];
const s = [];
for (const subkey in dk) {
if (subkey != "children") {
s.push(`data-${subkey}="${dk[subkey]}"`);
};
};
const sj = s.join(" ");
const icn = dk.icon ? dk.icon : dk.children ? "Folder.png" : "Item.png";
const src = me.imageBase + icn;
const children = dk.children ? me.walkData(dk.children) : "";
const bi = `<details>
<summary id="${key}" ${sj}>
<img class="icon" src="${src}"></img>
${dk.label}
</summary>
${children}
</details>`;
buf.push(bi);
};
Upvotes: 0
Reputation: 48693
I left your original code in-tact, I just added appropriate new-lines and indentation.
All I added was:
data
that I reverse engineered using the functionwalkData
function that just returns "CHILDREN"...Note: I could actually refine this code a bit and remove the closing tag for the <img>
element, since those are unnecessary. The subkey
map function is also a one-liner, so it can actually be converted to a lambda with not braces nor an explicit return
; just like the key
mapper outside of it.
I little bit of formatting goes a long way. Template literals are great, because you do not have to deal with a mess of string concatenation.
const data = {
'a': { label: 'Directory A', children: [] },
'b': { label: 'File B', children: null }
}
const me = {
imageBase: '/',
walkData: (children) => 'CHILDREN'
}
const buf = Object.keys(data).map((key) => `
<details>
<summary
id="${key}" ${Object.keys(data[key]).map((subkey) => {
return subkey != 'children'
? `data-${subkey}="${data[key][subkey]}"` : ' '
}).join(' ')}>
<img class="icon"
src="${me.imageBase}${data[key].icon
? data[key].icon : data[key].children
? 'Folder.png' : 'Item.png'}">
</img>
${data[key].label}
</summary>
${data[key].children ? me.walkData(data[key].children) : ""}
</details>
`)
document.getElementById('target').innerHTML = buf.join('')
<div id="target"></div>
Here is another version that has separate function calls and come comments. It is functionally the same as the previous code.
There are no explicit return
statements, since all the lambdas are all one-liners.
const data = {
'a': { label: 'Directory A', children: [] },
'b': { label: 'File B', children: null }
}
const me = {
imageBase: '/',
walkData: children => 'CHILDREN'
}
const main = () => {
document.getElementById('target').innerHTML = render(data)
}
const render = data =>
Object.keys(data).map(key =>
renderDetails(key, data)).join('')
const renderDetails = (key, data) =>
`<details>
<summary id="${key}" ${renderDataAttributes(data[key])}>
<img class="icon" src="${renderImageSource(data[key])}">
</img>
${data[key].label}
</summary>
${data[key].children ? me.walkData(data[key].children) : ""}
</details>`
const renderDataAttributes = detail =>
Object.keys(detail)
.filter(key => key !== 'children') // Filter-out children
.map(key => `data-${key}="${detail[key]}"`) // Map to a data attr
.join(' ') // Join the values
const renderImageSource = detail =>
me.imageBase + (
detail.icon /* If the detail has an icon, */
? detail.icon /* Use the defined icon */
: detail.children /* Else, does it have children? */
? 'Folder.png' /* If so, it's directory */
: 'Item.png' /* Else, it's a file */
)
main()
<div id="target"></div>
Upvotes: 1
Reputation: 334
Here, us this as a starting point:
const buf = []
for (const key in data) {
const t = Object.keys(data[key])
.map(subkey => {
return subkey != 'children'
? `data-${subkey}="${data[key][subkey]}"`
: ' '
})
.join(' ')
const u = `<details><summary id="${key}" ${t}><img class="icon" src="${
me.imageBase
}${
data[key].icon
? data[key].icon
: data[key].children
? 'Folder.png'
: 'Item.png'
}"> </img>${data[key].label}</summary>
${data[key].children ? me.walkData(data[key].children) : ''}</details>`
buf.push(u)
}
Upvotes: 1