Доступ к многоуровневому свойству и полный путь к свойствам

1

Я в настоящее время борется с проблемой JavaScript. Я хочу вернуть многоуровневое свойство, а также каждую переменную, содержащуюся внутри, путем передачи в исходном объекте и массива путей к свойствам, которые я хочу.

Например, если у меня есть следующий объект:

obj = {
  product: {
    candidate: {
      id: 10,
      reference: "test",
      count: 4,
      steps: 10
    }
  }
}

Я хочу иметь возможность вызвать метод:

getVarPath(obj, ["product.candidate.ID", "product.candidate.reference"])

И затем пусть он возвращает один объект с каждой переменной, переданной в массиве, в ней исходная структура. Таким образом, это вернет объект, выглядящий так:

{
  product: {
    candidate: {
      id: 10,
      reference: "test"
    }
  }
}

На данный момент у меня это работает в моем локальном решении (передавая только одну строку, а не массив).

Решение на данный момент довольно ужасное, но я хочу улучшить его, поэтому, если кто-нибудь может подумать о лучшем методе, который будет замечательным. Опять же, сейчас это довольно ужасно, но я хочу улучшить его. Но он выполняет эту работу:

var getVarPath = function(obj, keys){
  var elements = keys.split("."),
      evalStr = "",
      objStr = "obj",
      newObjStr = "newObj",
      newObj = {};

  if(elements.length > 1){

    elements.forEach(function(key, index){

      // first append a property accessor at the end of the eval string
      evalStr = evalStr + "['" + key + "']";

      // if we're at the last element, we've reached the value, so assign it
      if(index === elements.length -1){

        eval(newObjStr + evalStr + " = " + objStr + evalStr);
      }
      else {
        // if we're not at the last, we're at an object level
        // if the nested object doesn't exist yet, create it
        if(!eval(newObjStr + evalStr)){
          eval(newObjStr + evalStr + " = {};");
        }
      }
    });

  }

  return newObj;
}
  • 0
    Я отредактировал вопрос с моим текущим рабочим решением, но мне не нравится тот факт, что я использую eval, чтобы сделать его немного лучше
Теги:
object
data-manipulation
accessor

3 ответа

0

Используя _.propertyOf(), Array#reduce() и Object.assign(), а также имена вычисляемых свойств, вы можете создать менее сложную реализацию:

function getVarPath(object, paths) {
  return paths.reduce(function (accumulator, path) {
    const that = _.propertyOf(accumulator)
    let walk = path.split('.')
    let value = this(walk)

    for (let key = walk.pop(); key !== undefined; key = walk.pop()) {
      const base = that(walk)

      value = { [key]: value }

      if (base !== undefined) {
        value = Object.assign(base, value)
      }
    }

    return Object.assign(accumulator, value)
  }.bind(_.propertyOf(object)), {})
}

let obj = {
  product: {
    candidate: {
      id: 10,
      reference: "test",
      count: 4,
      steps: 10
    }
  }
}

console.log(getVarPath(obj, ['product.candidate.id', 'product.candidate.reference']))
<script src="https://cdn.rawgit.com/lodash/lodash/4.17.4/dist/lodash.min.js"></script>
0

Ваш код как есть, на самом деле не работает. Вы обрабатываете keys как строку, но передаете массив. Вы можете (и должны) избегать использования eval(), отслеживая "внутренние" объекты, которые вы сейчас просматриваете, и используя нотацию object[property] вместо object.property.

function getVarPath(obj, keys) {
  var result = {};

  // ["product.candidate.id", "product.candidate.reference"]
  keys.forEach(function(key) {
    var src = obj,     // inner source object
        dest = result, // inner destination object
        parts = key.split(/\./);

    // e.g. ["product", "candidate", "id"]
    parts.forEach(function(part) {

      // if we're looking at an object, make sure it exists in the dest
      if (typeof(src[part]) === "object")
        dest[part] = dest[part] || {};
      // if it just a value, copy it  
      else
        dest[part] = src[part];

      dest = dest[part]; // move from obj to obj.product, then to obj.product.candidate, etc.
      src = src[part];
    });
  });

  return result;
}

var obj = {
  product: {
    candidate: {
      id: 10,
      reference: "test",
      count: 4,
      steps: 10
    }
  }
}

var output = getVarPath(obj, ["product.candidate.id", "product.candidate.reference"]);

console.log(JSON.stringify(output));
0

Для каждого элемента входного массива:

Во-первых, вы можете разбить начальную строку: var nestedElements="product.candidate.ID".split(.)"
Это возвращает массив с каждым уровнем: ["product","candidate","ID"]

Теперь вы можете получить доступ к вашему вложенному объекту с помощью каждого элемента массива: obj["product"]["candidate"]["ID"] либо с помощью цикла по массиву, либо рекурсии.

var currentobj=obj;
for (var i=0;i<nestedElements.length;i++){
  currentobj=currentobj[nestedElements[i]]
}
// currentobj is your id      

В этом же процессе вы можете динамически добавлять элементы в новый объект с помощью аналогичного процесса:

newobj={} //before loop
if (newobj["product"] === undefined) newobj["product"]={} //in loop

И это должно быть сделано для каждого элемента входного массива, в конце - итерации через массивы и доступа к объекту с использованием строк

  • 0
    Эй, спасибо за твой ответ! Я попытался реализовать твой пример здесь, но он, похоже, не работает для меня, я получаю исключения, когда он пытается углубиться глубже, чем в один элемент, ты сам сделал это?
  • 0
    Этот пример в основном псевдокод с базовой логикой, а не с рабочим кодом, вам нужно его адаптировать. Какие исключения вы получаете?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню