简体   繁体   English

将数组转为更深层次的对象

[英]Turn array to a deep level of object

I have an array and a sample object like this:我有一个数组和一个这样的示例对象:

const array = ['a', 'b.c', 'c.d.e']

Sample样本

const sample = {
  'aa': 'test',
  'a': 'value',
  'b': {
    'c': 'value'
  },
  'c': {
    'd': {
      'e': 'value'
    }
  },
  'e': {
    'f': {
      'g': {
        'h' : 'value'
      }
    }
  }
}

Then I want to make a new object basing on the sample.然后我想根据样本制作一个新对象。 The result should look like this:结果应如下所示:

const newSample = {
  'aa': 'test',
  'a': 'oldvalue' + 1,
  'b': {
    'c': 'oldvalue' + 1
  },
  'c': {
    'd': {
      'e': 'oldvalue' + 1
    }
  },
  'e': {
    'f': {
      'g': {
        'h' : 'oldvalue'
      }
    }
  }
}

I'm thinking of loop through the array and count the length of each element.我正在考虑遍历数组并计算每个元素的长度。 However, it's not as efficient as the level of the array and sample increase.但是,它不如数组和样本的级别增加那么有效。 Are there any algorithms that can be done better?有没有可以做得更好的算法?

 const array = ['a', 'b.c', 'cde'] const sample = { 'aa': test, 'a': 'value', 'b': { 'c': 'value' }, 'c': { 'd': { 'e': 'value' } }, 'e': { 'f': { 'g': { 'h' : 'value' } } } } const newSample = {} const transformer = (array) => { array.forEach(item => { const itemArr = item.split('.') if (itemArr.length === 1) { console.log(sample[itemArr[0]]) newSample[itemArr[0]] = sample[itemArr[0]] + 1 } // the condition goes on... }) } transformer(array) console.log(newSample)

Thanks,谢谢,

You could reduce the splitted keys and save the last key for the assignment with the last key.您可以减少拆分的键并使用最后一个键保存分配的最后一个键。

 const keys = ['a', 'b.c', 'cde'] object = { a: 'value', b: { c: 'value' }, c: { d: { e: 'value' } }, e: { f: { g: { h : 'value' } } } }, transformer = (objec, keys) => keys.reduce((r, s) => { var path = s.split('.'), last = path.pop(), final = path.reduce((o, k) => o[k] = o[k] || {}, r); final[last] = (final[last] || '') + 1; // or whatever you need change return r; }, object); console.log(transformer(object, keys));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

Nonmutating approach by returning a deep copy with changed value.通过返回具有更改值的深层副本来实现非变异方法。

 function transformer(object, keys) { return Object.entries(object).reduce((r, [k, v]) => { r[k] = v && typeof v === 'object' ? transformer(v, keys.filter(s => s.startsWith(k + '.')).map(s => s.split('.').slice(1).join('.'))) : keys.includes(k) ? (v || '') + 1: v; return r; }, {}); } const keys = ['a', 'b.c', 'cde'] object = { a: 'value', b: { c: 'value' }, c: { d: { e: 'value' } }, e: { f: { g: { h : 'value' } } } }; console.log(transformer(object, keys));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

It looks like you just want to apply a new value to a set of scoped-fields.看起来您只想将新值应用于一组范围字段。

You could just loop over your desired scoped-fields and set a new value for them.您可以遍历所需的范围字段并为它们设置一个新值。 The answer to the following question: Convert JavaScript string in dot notation into an object reference works very well for this problem set.以下问题的答案: 将点表示法中的 JavaScript 字符串转换为对象引用非常适合此问题集。

Warnings警告

If you need to deep-copy or clone the object, you can serialize-deserialize it:如果您需要深度复制或克隆对象,您可以对其进行序列化-反序列化:

function cloneObject(obj) {
  return JSON.parse(JSON.stringify(obj));
}

Normal assignment does NOT handle deep-copies:普通分配处理深副本:

function cloneObject(obj) {
  return Object.assign({}, obj);
}

 const array = ['a', 'b.c', 'cde', 'efgh']; const sample = { 'a': 'value', 'b': { 'c': 'value' }, 'c': { 'd': { 'e': 'value' } }, 'e': { 'f': { 'g': { 'h': 'value' } } } }; const newSample = setValueForKeys(cloneObject(sample), array, 'someValue'); console.log('Updated:', newSample); console.log('Original:', sample); function setValueForKeys(source, keys, value) { keys.forEach((key) => index(source, key, value)); return source; } function cloneObject(obj) { return JSON.parse(JSON.stringify(obj)); } // See: https://stackoverflow.com/a/6394168/1762224 function index(obj, is, value) { if (typeof is == 'string') return index(obj, is.split('.'), value); else if (is.length == 1 && value !== undefined) return obj[is[0]] = value; else if (is.length == 0) return obj; else return index(obj[is[0]], is.slice(1), value); } function multiIndex(obj, is) { return is.length ? multiIndex(obj[is[0]], is.slice(1)) : obj; } function pathIndex(obj, is) { return multiIndex(obj, is.split('.')) }
 .as-console-wrapper { top: 0; max-height: 100% !important; }

You can do this with forEach loop to loop each element in the array and then split each path to array and then use reduce method on that path array to update each nested property.您可以使用forEach循环来循环数组中的每个元素,然后split每个路径split为数组,然后在该路径数组上使用reduce方法来更新每个嵌套属性。

 const array = ['a', 'b.c', 'cde'] const sample = {"a":"value","b":{"c":"value"},"c":{"d":{"e":"value"}},"e":{"f":{"g":{"h":"value"}}}} function update(array, sample) { array.forEach(c => { c.split('.').reduce((r, e, i, a) => { if (!a[i + 1]) r[e] = r[e] + 1 else return r[e] || {} }, sample) }) } update(array, sample); console.log(sample)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM