英文:
Split array object to nested array for each separator
问题
我正在尝试拆分以下数组:
{
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b"
}
转换成类似以下的格式:
{
"Base": {
"Brand": {
"0101-color-brand-primary-red": "#fe414d",
"0106-color-brand-secondary-green": "#00e6c3",
"0102-color-brand-primary-light-gray": "#eaecf0",
"0107-color-brand-secondary-black": "#000000",
"0103-color-brand-primary-white": "#ffffff",
"0108-color-brand-secondary-dark-gray": "#b4b4b4",
"0104-color-brand-secondary-blue": "#079fff",
"0105-color-brand-secondary-yellow": "#ffe63b",
"Light": {
"Extended": {
"Red": {
"0201-color-extended-900-red": "#7f1d1d",
"0202-color-extended-800-red": "#991b1b"
}
}
}
}
}
}
基本上我需要按照 '/' 拆分数组并创建嵌套数组。请帮忙指导如何实现。
英文:
I am trying to split the below array
{
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b"
}
to something like this
{
"Base": {
"Brand": {
"0101-color-brand-primary-red": "#fe414d",
"0106-color-brand-secondary-green": "#00e6c3",
"Light": {
"Extended": {
"Red": {
"0201-color-extended-900-red": "#7f1d1d",
"0202-color-extended-800-red": "#991b1b"
}
}
}
}
}
}
Basically I need to split array by '/' and create nested array
Please help how can I achieve this.
答案1
得分: 1
使用 Object.entries
获取原始对象中的所有条目。使用 split('/')
获取输出中对象路径键的数组。然后在该路径数组上使用 reduce
创建结果中的级别,直到达到路径中的最后一个元素。在此时,我们将最后一个路径元素设置为所需的颜色值。
英文:
Use Object.entries
to get all entries in the original object. Use split('/')
to get an array of the object path keys in the output. Then use reduce
on that path array to create levels inside the result, until the very last element in the path is reached. At this point, we set the last path element to the required color value.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const d = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b"
}
const r = Object.entries(d).reduce((a, ) =>
(p.split('/').reduce((a,c,i,r) =>
a[c] ??= i<(r.length-1) ? {} : v, a), a), {})
console.log(r)
<!-- end snippet -->
答案2
得分: 1
由于提供的结构是一个扁平(非嵌套)的键值对(条目)对象,可以通过reduce
对源对象的entries
进行聚合,以开始构建 OP 所需的目标数据结构。
这个外部 reduce
任务的初始值将是一个空对象(字面量)。
这个任务的reducer 函数在每次迭代时都会接收到要进行聚合的对象引用(在最开始时是初始值/空对象)。它还接收到当前迭代的 entry,这是一个作为数组提供的键值对。key
实际上是 键路径,是一个字符串,可以在每次出现的斜杠处进一步进行 split
。value
是 路径值,必须分配为任何聚合(部分)对象引用的最后一个值。
因此,需要一个第二个嵌套的 reduce
任务,它会处理每个键路径的部分 key
值。内部 reduce
任务的初始值始终是要聚合的对象的基本或根引用。内部 reduce
任务通过 访问已存在的节点,创建新的(空节点)或分配 最终路径值到最后创建的节点来聚合接收到的 node
引用。它还始终将此引用作为当前节点传递给下一个迭代步骤。
const sampleData = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b",
};
function createObjectFromKeyPartialsAndValues(data) {
return Object
.entries(data)
.reduce((root, [keyPath, pathValue]) => {
keyPath
.split('/')
.reduce((node, key, idx, keyList) => {
const isLastKey = !keyList.hasOwnProperty(idx + 1);
const value = isLastKey ? pathValue : {};
const obj = (node[key] ??= value);
return obj;
}, root);
return root;
}, {});
}
console.log(
createObjectFromKeyPartialsAndValues(sampleData)
);
英文:
Since the provided structure is a flat (not nested) object of key-value pairs (entries), one could start aggregating the OP's desired target data-structure by reduce
ing the source-object's entries
.
The initial value of this outer reduce
task will be an empty object (literal).
The reducer function of the very same task does receive with each iteration the to be aggregated object reference (which in the very beginning is the initial value / the empty object). It also receives the current iteration's entry which is a key-value pair provided as an array. The key
actually is the key-path, a string which can be further split
at every occurring slash. The value
is the path-value which has to be assigned as last value of any aggregated (partial) object reference.
Thus, one is in need of a second, nested, reduce
task which does process each of the key-path' partial key
values. The initial value will be always the base or root reference of the to be aggregated object. The inner reduce
task does aggregate the received node
reference by either accessing an already existing node, or by creating a new (empty node) or by assigning the final path-value to the last created node. It also always does pass this reference as current node into the next iteration step.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const sampleData = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b",
};
function createObjectFromKeyPartialsAndValues(data) {
return Object
.entries(data)
.reduce((root, [keyPath, pathValue]) => {
keyPath
.split('/')
.reduce((node, key, idx, keyList) => {
const isLastKey = !keyList.hasOwnProperty(idx + 1);
const value = isLastKey ? pathValue : {};
const obj = (node[key] ??= value);
return obj;
}, root);
return root;
}, {});
}
console.log(
createObjectFromKeyPartialsAndValues(sampleData)
);
<!-- language: lang-css -->
.as-console-wrapper { min-height: 100%!important; top: 0; }
<!-- end snippet -->
答案3
得分: 1
以下是翻译好的部分:
首先,在将来,请在提问时分享您自己的努力。如果这里已经有足够好的答案,我个人不会回答,直到我看到您自己的尝试。
我保留了一些实用函数。其中之一是hydrate,它接受一个我称之为pathEntries
的数组(类似于Object.entries
的结果,但是用一个表示路径的字符串/整数的数组,而不是属性的单个字符串),并返回一个对象。有了这个,我们可以编写一个非常简单的版本的这个函数:
const expand = (o) =>
hydrate(Object.entries(o).map(([k, v]) => [k.split('/'), v]))
hydrate
基于setPath
,它创建一个对象的副本(尽可能共享),在给定路径上放置一个新值,同时创建新节点。
例如,
setPath
(['foo', 'bar', 0, 'baz'])
(42)
({foo: {qux: {corge: 100, grault: 99}}, waldo: 0})
//=> {foo: {bar: [{baz: 42}], qux: {corge: 100, grault: 99}}, waldo: 0}
综合起来,我的解决方案看起来像这样:
const setPath = ([p, ...ps]) => (v) => (o) =>
p == undefined ? v : Object.assign(
Array.isArray(o) || Number.isInteger(p) ? [] : {},
{...o, [p]: setPath(ps)(v)((o || {})[p])}
)
const hydrate = (xs) =>
xs.reduce((a, [p, v]) => setPath(p)(v)(a), {})
const expand = (o) =>
hydrate(Object.entries(o).map(([k, v]) => [k.split('/'), v]))
const flatData = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b"
}
console.log(expand(flatData))
<details>
<summary>英文:</summary>
First off, in the future, please share your own effort when asking a question. I for one wouldn't answer until I saw your own approach if there weren't already decent answers here.
-----------
I keep handy a number of utility functions. One of those is [hydrate][hy], which accepts an array of what I call `pathEntries` (similar to the result of `Object .entries`, but with an *array* of strings/integers representing the *path* rather than a single string for the property) and returns an object. With that, we can write a very simple version of this function:
```js
const expand = (o) =>
hydrate (Object .entries (o) .map (([k, v]) => [k.split('/'), v]))
hydrate
is based on setPath
which creates a copy (sharing as much as possible) of an object, with a new value at the given path, creating new nodes along the way.
For example,
setPath
(['foo', 'bar', 0, 'baz'])
(42)
({foo: {qux: {corge: 100, grault: 99}}, waldo: 0})
//=> {foo: {bar: [{baz: 42}], qux: {corge: 100, grault: 99}}, waldo: 0}
Put together, my solution would look like this:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const setPath = () => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o,
: setPath (ps) (v) ((o || {})
)}
)
const hydrate = (xs) =>
xs .reduce ((a,
) => setPath (p) (v) (a), {})
const expand = (o) =>
hydrate (Object .entries (o) .map (([k, v]) => [k.split('/'), v]))
const flatData = {"Base/Brand/0101-color-brand-primary-red": "#fe414d", "Base/Brand/0106-color-brand-secondary-green": "#00e6c3", "Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0", "Base/Brand/0107-color-brand-secondary-black": "#000000", "Base/Brand/0103-color-brand-primary-white": "#ffffff", "Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4", "Base/Brand/0104-color-brand-secondary-blue": "#079fff", "Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d", "Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b", "Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b"}
console .log (expand (flatData))
<!-- language: lang-css -->
.as-console-wrapper {max-height: 100% !important; top: 0}
<!-- end snippet -->
答案4
得分: 0
以下是代码部分的翻译:
你可以通过像下面这样的递归函数来实现这个目标。它的作用是通过“/”分割每个路径,然后以递归方式检查它们,如果它们尚未附加到输出对象中,就将每个部分添加到输出对象中。
const list = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b",
};
const result = {};
Object.entries(list).forEach((entry) => {
const path = entry[0]; // "Base/Brand/0101-color-brand-primary-red"
const value = entry[1]; // "#fe414d"
sections = path.split("/"); // ["Base", "Brand", "0101-color-brand-primary-red"]
// 开始使用递归函数创建输出对象
sett(result, sections, sections[0], 0, sections.length, value);
});
function sett(parentObj, sections, currentSection, currentIndex, depth, value) {
// 如果已经到达路径的末尾,就停止
if (currentIndex >= depth) return;
// 如果不是末尾,检查这是否是路径的最后一部分
const isLastSection = sections[currentIndex + 1] === undefined;
// 如果结果中没有这个部分的条目(例如“Base”或“brand”)
if (!parentObj[currentSection])
// 如果是路径的末尾(例如像0101-color-brand-primary-red这样的颜色之一),则将其分配为值,否则将其分配为{}
parentObj[currentSection] = isLastSection ? value : {};
// 否则继续深入路径
const nextSection = sections[currentIndex + 1];
sett(
parentObj[currentSection],
sections,
nextSection,
currentIndex + 1,
depth,
value
);
}
console.log(result);
希望这能帮助你理解这段代码。如果有任何问题,请随时提出。
英文:
You can achieve that with a recursive function like the one below. What it does is to split each path by "/" and then check them in a recursive manner and add each section to the output object if they're not already appended.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const list = {
"Base/Brand/0101-color-brand-primary-red": "#fe414d",
"Base/Brand/0106-color-brand-secondary-green": "#00e6c3",
"Base/Brand/0102-color-brand-primary-light-gray": "#eaecf0",
"Base/Brand/0107-color-brand-secondary-black": "#000000",
"Base/Brand/0103-color-brand-primary-white": "#ffffff",
"Base/Brand/0108-color-brand-secondary-dark-gray": "#b4b4b4",
"Base/Brand/0104-color-brand-secondary-blue": "#079fff",
"Base/Light/Extended/Red/0201-color-extended-900-red": "#7f1d1d",
"Base/Brand/0105-color-brand-secondary-yellow": "#ffe63b",
"Base/Light/Extended/Red/0202-color-extended-800-red": "#991b1b",
};
const result = {};
Object.entries(list).forEach((entry) => {
const path = entry[0]; // "Base/Brand/0101-color-brand-primary-red"
const value = entry[1]; // "#fe414d"
sections = path.split("/"); // ["Base", "Brand", "0101-color-brand-primary-red"]
// start creating the output object using a recursive function
sett(result, sections, sections[0], 0, sections.length, value);
});
function sett(parentObj, sections, currentSection, currentIndex, depth, value) {
// stop if we've reached the end of the path
if (currentIndex >= depth) return;
// if not, check if this is the last section of the path
const isLastSection = sections[currentIndex + 1] === undefined;
// if there is not already an entry in result for this section (e.g "Base" or "brand")
if (!parentObj[currentSection])
// if it's the end of the path (e.g one of the colors like 0101-color-brand-primary-red) assign it the value else assign {} to it
parentObj[currentSection] = isLastSection ? value : {};
// else go deeper in the path
const nextSection = sections[currentIndex + 1];
sett(
parentObj[currentSection],
sections,
nextSection,
currentIndex + 1,
depth,
value
);
}
console.log(result);
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论