Доступ к нескольким вложенным объектам

Я делаю глобальное хранилище данных для своего приложения (приложение Angular JS, но это вопрос о JS, а не об Angular).

Я настроил service, который устанавливает данные, получает данные и т. д.

Это выглядит так:

angular.module('core').factory('dataService', function(callsService) {
  let properties = {
    globalData: {}
  };

  properties.insertData = function(data) {
    for (let x in data) {
      this.globalData[x] = data[x];
    }
    return;
  }

  properties.getData = function(data) {
    return this.globalData[data];
  }

  return properties;
});

Использование сервиса будет выглядеть так:

dataService.insertData({foo: 'bar'});
dataService.getData('foo'); // 'bar'

Однако это становится проблемой, когда есть вложенные данные, например:

dataService.insertData({foo: {bar: 'hello world'}});
dataService.getData('foo'); // {bar: 'hello world'}

Очевидно, именно так будут работать ссылки на объекты, но как я могу передать что-то вроде:

dataService.getData('foo.bar'); // 'hello world'

or

dataService.getData('[foo][bar]'); // 'hello world'

Возвращаясь к моему методу properties.getData, есть ли способ рекурсивно (или каким-либо другим образом) получить доступ к вложенным объектам?

properties.getData = function(data) {
  return this.globalData[data]; // needs to be able to get nested objects
}

person 4lackof    schedule 17.06.2018    source источник
comment
Возможный дубликат Доступ/обработка (вложенных) объектов, массивов или JSON.   -  person georgeawg    schedule 17.06.2018


Ответы (4)


Обновленный ответ:

Я думаю, что этот рекурсивный лайнер сделает именно то, что вы ищете:

properties.getData = (args, data) => args.length ? this.getData(args.slice(1), data ? data[args[0]] : this.globalData[args[0]]) : data ? data : this.globalData

Вызовите его с вашим бесконечным количеством аргументов свойств или индексов массива в массиве следующим образом:

dataService.getData(['arrayOfData', 2, 'propOnArrElement', 'subProp', 'desiredValue'])

Пояснение:

"Подпись" функции будет выглядеть примерно так

getData(args: Array, data?: any): any,

что значит:

  1. Он принимает массив в качестве первого аргумента (содержащий след свойств/индексов, которые вы хотите пройти, чтобы добраться до ваших вложенных данных)
  2. Он принимает структуру данных, которую мы запрашиваем, в качестве необязательного второго аргумента (конечно, это обязательно должен быть объект или массив).
  3. Он вернет желаемый фрагмент данных, который может быть любого типа.

Как это работает:

Когда функция вызывается,

  • второй аргумент не должен быть определен, чтобы служба могла обращаться к объекту globalData, как мы увидим позже.
  • The args Array is checked for length (args.length ?), and
    • if it has elements, the recursive call is made (this.getData(),
      • but this time, we will drop the first arg from the args Array (args.slice(1),),
      • and the data argument will be (re)included (data ?).
        • If it was defined in the most recent recursive call, then the first arg element is used to access that property(or index) on the data Object/Array (data[args[0]] :),
        • но если оно еще не определено (как при первоначальном вызове функции), рекурсивный вызов вместо этого будет использовать свойство globalData (this.globalData[args[0]])).
    • As the function continues it's recursive course, the data structure is being narrowed down to deeper levels of data, and the arguments list is dwindling.
      • Once the args length resolves to false (no more args left to explore!), instead of returning another recursive call, the function simply returns the current data object (: data ? data).
      • Если эта функция была вызвана с пустым массивом, будут возвращены все глобальные данные (: this.globalData).

Я надеюсь, что вы найдете это полезным или, по крайней мере, приятным чтением. Я был очень взволнован, когда это решение пришло мне в голову, когда я принимал душ сегодня вечером. :П

Дополнительный материал

Вот аналогичный (даже лучший) подход, использующий остальные параметры ES6 и Array.reduce (это позволит вам вызывать функцию без передачи массива):

properties.getData = (...args) => args.reduce((data, arg) => data[arg], this.globalData)

вызовите так:

dataService.getData('firstarg', 'secondarg', 'onemorearg', 'desireddata')

Исходный, неудовлетворительный ответ:

Поскольку вы возвращаете данные, вы можете просто получить доступ к свойству непосредственно при вызове функции:

const nestedVal = dataService.getData(‘foo’).bar

Or:

const nestedVal = dataService.getData(‘foo’)[‘bar’]
person Ben Steward    schedule 17.06.2018
comment
есть этот вариант, однако я надеялся немного абстрагировать действие (просто передать всю ссылку на getData и вернуть то, что мне нужно - person 4lackof; 17.06.2018

Кажется, это два вопроса в одном.

В ответ на ваш первый вопрос (передавая что-то вроде dataService.getData('foo.bar');

Вы можете получить доступ к свойствам объекта с помощью таких строк:

var property = foo['bar'];

Вы можете сделать это рекурсивным, если хотите, например:

var property = foo['bar']['baz'];

Что касается вашего второго вопроса, это может быть дубликат this ?

person Jessica    schedule 17.06.2018

Воспользуйтесь услугой $parse:

  var object = { 'a': [{ 'b': { 'c': 3 } }] };
  var accessor= 'a[0].b.c';

  var result = $parse(accessor)(object);
  console.log("result =>",result);

  // result => 3 

ДЕМО

angular.module("app",[])
.run(function($parse) {

  var object = { 'a': [{ 'b': { 'c': 3 } }] };
  var accessor= 'a[0].b.c';
  
  var result = $parse(accessor)(object);
  console.log("result =>",result);
  
  // result => 3 
})
 <script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
</body>

Для получения дополнительной информации см. Справочник по API службы AngularJS $parse.

person georgeawg    schedule 17.06.2018

Если бы я мог предложить использовать функцию стрелки в качестве селектора, что-то вроде:

dataService.getData(storage => storage.foo.bar);

И функция внутри вашего сервиса будет:

properties.getData = function (dataSelector) {
    return dataSelector(this.globalData);
} 

А вот рабочий пример:

const data = {
  prop1: {
    prop2: 1
  }
};

function getData(dataSelector) {
  return dataSelector(data);
}

const prop2 = getData(data => data.prop1.prop2);

console.log(prop2);

В противном случае вам пришлось бы использовать регулярное выражение для разделения строки, например:

properties.getData = function(data) {  
  var props = data.replace(/\]/g,'').split(/\.\[/g);

 return props.reduce((obj, prop) => obj[prop], this.globalData);

} 

Для дальнейшего ознакомления эта статья может быть полезна

person J. Pichardo    schedule 17.06.2018
comment
На селекторе функций со стрелками я получаю сообщение об ошибке, что dataSelector не является функцией. Когда вы передаете функцию стрелки, должно ли storage.foo.bar быть строковым? Кроме того, у вас есть ссылка на полную документацию о том, как это будет работать (со стрелочными функциями)? Я хотел бы полностью прочитать об этом. - person 4lackof; 17.06.2018
comment
@4lackof К сожалению, я сижу на своем телефоне, поэтому не могу сделать фрагмент, однако вы можете прочитать о функциях стрелок в MDN, я обновлю ответ как можно скорее. - person J. Pichardo; 17.06.2018
comment
@4lackof я обновил ответ реферальной статьей - person J. Pichardo; 17.06.2018
comment
@4lackof Я добавил рабочий пример для функции стрелки - person J. Pichardo; 18.06.2018