JSON найти в JavaScript

Есть ли лучший способ, кроме циклического поиска данных в JSON? Это для редактирования и удаления.

for(var k in objJsonResp) {
  if (objJsonResp[k].txtId == id) {
    if (action == 'delete') {
      objJsonResp.splice(k,1);
    } else {
      objJsonResp[k] = newVal;
    }
    break;
  }
}

Данные расположены в виде списка карт. Подобно:

[
  {id:value, pId:value, cId:value,...},
  {id:value, pId:value, cId:value,...},
  ...
]

5 ответов

Решение

(Вы ищете не в "JSON", а в массиве - строка JSON уже десериализована в граф объектов, в данном случае массив.)

Некоторые варианты:

Используйте объект вместо массива

Если вы контролируете генерацию этой вещи, должен ли это быть массив? Потому что, если нет, есть гораздо более простой способ.

Скажите, что это ваши исходные данные:

[
    {"id": "one",   "pId": "foo1", "cId": "bar1"},
    {"id": "two",   "pId": "foo2", "cId": "bar2"},
    {"id": "three", "pId": "foo3", "cId": "bar3"}
]

Не могли бы вы сделать следующее вместо этого?

{
    "one":   {"pId": "foo1", "cId": "bar1"},
    "two":   {"pId": "foo2", "cId": "bar2"},
    "three": {"pId": "foo3", "cId": "bar3"}
}

Тогда поиск соответствующей записи по идентификатору тривиален:

id = "one"; // Or whatever
var entry = objJsonResp[id];

... как обновляет его:

objJsonResp[id] = /* New value */;

... и удаляя его:

delete objJsonResp[id];

Это использует тот факт, что в JavaScript вы можете индексировать в объект, используя имя свойства в качестве строки - и эта строка может быть литералом, или она может быть из переменной, как с id выше.

Ввод карты ID-to-Index

(Тупая идея, предшествующая вышесказанному. Сохранена по историческим причинам.)

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

[
    {"id": "one",   "pId": "foo1", "cId": "bar1"},
    {"id": "two",   "pId": "foo2", "cId": "bar2"},
    {"id": "three", "pId": "foo3", "cId": "bar3"}
]

Генерирующий код может предоставить карту id-to-index:

{
    "index": {
        "one": 0, "two": 1, "three": 2
    },
    "data": [
        {"id": "one",   "pId": "foo1", "cId": "bar1"},
        {"id": "two",   "pId": "foo2", "cId": "bar2"},
        {"id": "three", "pId": "foo3", "cId": "bar3"}
    ]
}

Затем получить запись для идентификатора в переменной id тривиально:

var index = objJsonResp.index[id];
var obj = objJsonResp.data[index];

Это использует тот факт, что вы можете индексировать объекты, используя имена свойств.

Конечно, если вы сделаете это, вы должны обновить карту при изменении массива, что может стать проблемой обслуживания.

Но если вы не контролируете генерацию объекта или обновление карты ids-to-indexes - это слишком много кода и / или проблем с обслуживанием, то вам придется выполнить поиск методом перебора.

Поиск грубой силы (исправлено)

Отчасти OT (хотя вы спрашивали, есть ли лучший способ:-)), но ваш код для цикла по массиву неверен. Подробности здесь, но вы не можете использовать for..in циклически проходить по индексам массивов (точнее, если вы это сделаете, вы должны приложить особые усилия для этого); for..in перебирает свойства объекта, а не индексы массива. Ваша лучшая ставка с не разреженным массивом (а ваш не разреженным) - это стандартный старомодный цикл:

var k;
for (k = 0; k < someArray.length; ++k) { /* ... */ }

или же

var k;
for (k = someArray.length - 1; k >= 0; --k) { /* ... */ }

Что бы вы ни предпочли (последняя не всегда быстрее во всех реализациях, что для меня нелогично, но мы здесь). (С разреженным массивом вы можете использовать for..in но снова предпринимаем особые усилия, чтобы избежать ловушек; больше в статье, указанной выше.)

С помощью for..in в массиве, кажется, работает в простых случаях, потому что массивы имеют свойства для каждого из своих индексов и только свои другие свойства по умолчанию (length и их методы) помечены как не перечисляемые. Но он ломается, как только вы устанавливаете (или задаете фреймворк) любые другие свойства объекта массива (что вполне допустимо; массивы - это просто объекты с небольшой специальной обработкой вокруг length имущество).

Я сталкивался с этой проблемой для сложной модели с несколькими вложенными объектами. Хороший пример того, на что я смотрел, - вот что: допустим, у вас есть поляроид. И эта картина затем помещается в багажник автомобиля. Машина внутри большого ящика. Ящик находится в трюме большого корабля со многими другими ящиками. Мне пришлось обыскать трюм, посмотреть в ящиках, проверить багажник, а затем найти существующую фотографию со мной.

Я не мог найти какие-либо хорошие решения в Интернете для использования и использования .filter() работает только на массивах. Большинство решений предложили просто проверить, model["yourpicture"] существовала. Это было очень нежелательно, потому что на примере это обыскало бы трюм корабля, и мне нужен был способ получить их дальше от кроличьей норы.

Это рекурсивное решение, которое я сделал. В комментариях я подтвердил от TJ Crowder, что потребуется рекурсивная версия. Я думал, что поделюсь этим, если кто-нибудь столкнется с подобной сложной ситуацией.

function ContainsKeyValue( obj, key, value ){
    if( obj[key] === value ) return true;
    for( all in obj )
    {
        if( obj[all] != null && obj[all][key] === value ){
            return true;
        }
        if( typeof obj[all] == "object" && obj[all]!= null ){
            var found = ContainsKeyValue( obj[all], key, value );
            if( found == true ) return true;
        }
    }
    return false;
}

Это начнется с заданного объекта внутри графа и найдет все найденные объекты. Я использую это так:

var liveData = [];
for( var items in viewmodel.Crates )
{
    if( ContainsKeyValue( viewmodel.Crates[items], "PictureId", 6 ) === true )
    {
        liveData.push( viewmodel.Crates[items] );
    }
}

Который будет производить массив ящиков, которые содержали мою картину.

Zapping - вы можете использовать эту библиотеку JavaScript; DefiantJS. Нет необходимости реструктурировать данные JSON в объекты для облегчения поиска. Вместо этого вы можете искать структуру JSON с помощью выражения XPath, например:

    var data = [
   {
      "id": "one",
      "pId": "foo1",
      "cId": "bar1"
   },
   {
      "id": "two",
      "pId": "foo2",
      "cId": "bar2"
   },
   {
      "id": "three",
      "pId": "foo3",
      "cId": "bar3"
   }
],
res = JSON.search( data, '//*[id="one"]' );

console.log( res[0].cId );
// 'bar1'

DefiantJS расширяет глобальный объект JSON новым методом; "поиск", который возвращает массив с совпадениями (пустой массив, если ничего не найдено). Вы можете попробовать это сами, вставив свои данные JSON и протестировав различные запросы XPath здесь:

http://www.defiantjs.com/

XPath, как вы знаете, является стандартизированным языком запросов.

ОК. Итак, я знаю, что это старый пост, но, возможно, это может помочь кому-то другому. Это не имеет обратной совместимости, но это почти не имеет значения, поскольку Internet Explorer становится избыточным.

Самый простой способ сделать именно то, что нужно:

function findInJson(objJsonResp, key, value, aType){        
     if(aType=="edit"){
        return objJsonResp.find(x=> x[key] == value);
     }else{//delete
         var a =objJsonResp.find(x=> x[key] == value);
         objJsonResp.splice(objJsonResp.indexOf(a),1);
     }
}

Он вернет элемент, который вы хотите отредактировать, если вы укажете "редактировать" в качестве типа. Поставьте что-нибудь еще или ничего, и это предполагает удаление. Вы можете перевернуть условные выражения, если хотите.

Если данные JSON в вашем массиве отсортированы каким-либо образом, вы можете выполнить различные поиски. Однако, если вы не имеете дело с большим количеством данных, то вам, вероятно, будет хорошо с операцией O(n) здесь (как у вас). Все остальное, вероятно, будет излишним.

Общее решение

Мы используем сканирование объектов для обработки большого количества данных. У него есть несколько хороших свойств, особенно обход в безопасном порядке удаления. Вот как можно реализовать поиск, удаление и замену для вашего вопроса.

const objectScan = require('object-scan');

const tool = (() => {
  const scanner = objectScan(['[*]'], {
    abort: true,
    rtn: 'bool',
    filterFn: ({
      value, parent, property, context
    }) => {
      if (value.id === context.id) {
        context.fn({ value, parent, property });
        return true;
      }
      return false;
    }
  });
  return {
    add: (data, id, obj) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property + 1, 0, obj) }),
    del: (data, id) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property, 1) }),
    mod: (data, id, prop, v = undefined) => scanner(data, {
      id,
      fn: ({ value }) => {
        if (value !== undefined) {
          value[prop] = v;
        } else {
          delete value[prop];
        }
      }
    })
  };
})();

// -------------------------------

const data = [
  { id: 'one', pId: 'foo1', cId: 'bar1' },
  { id: 'three', pId: 'foo3', cId: 'bar3' }
];
const toAdd = { id: 'two', pId: 'foo2', cId: 'bar2' };

const exec = (fn) => {
  console.log('---------------');
  console.log(fn.toString());
  console.log(fn());
  console.log(JSON.stringify(data));
};

exec(() => tool.add(data, 'one', toAdd));
exec(() => tool.mod(data, 'one', 'pId', 'zzz'));
exec(() => tool.mod(data, 'one', 'other', 'test'));
exec(() => tool.mod(data, 'one', 'gone', 'delete me'));
exec(() => tool.mod(data, 'one', 'gone'));
exec(() => tool.del(data, 'three'));

// ---------------
// () => tool.add(data, 'one', toAdd)
// true
// [{"id":"one","pId":"foo1","cId":"bar1"},{"id":"two","pId":"foo2","cId":"bar2"},{"id":"three","pId":"foo3","cId":"bar3"}]
// ---------------
// () => tool.mod(data, 'one', 'pId', 'zzz')
// true
// [{"id":"one","pId":"zzz","cId":"bar1"},{"id":"two","pId":"foo2","cId":"bar2"},{"id":"three","pId":"foo3","cId":"bar3"}]
// ---------------
// () => tool.mod(data, 'one', 'other', 'test')
// true
// [{"id":"one","pId":"zzz","cId":"bar1","other":"test"},{"id":"two","pId":"foo2","cId":"bar2"},{"id":"three","pId":"foo3","cId":"bar3"}]
// ---------------
// () => tool.mod(data, 'one', 'gone', 'delete me')
// true
// [{"id":"one","pId":"zzz","cId":"bar1","other":"test","gone":"delete me"},{"id":"two","pId":"foo2","cId":"bar2"},{"id":"three","pId":"foo3","cId":"bar3"}]
// ---------------
// () => tool.mod(data, 'one', 'gone')
// true
// [{"id":"one","pId":"zzz","cId":"bar1","other":"test"},{"id":"two","pId":"foo2","cId":"bar2"},{"id":"three","pId":"foo3","cId":"bar3"}]
// ---------------
// () => tool.del(data, 'three')
// true
// [{"id":"one","pId":"zzz","cId":"bar1","other":"test"},{"id":"two","pId":"foo2","cId":"bar2"}]

Если вы делаете это более чем в одном месте в вашем приложении, имеет смысл использовать клиентскую базу данных JSON, потому что создание пользовательских функций поиска является грязным и менее обслуживаемым, чем альтернатива.

Проверьте ForerunnerDB, который предоставляет вам очень мощную клиентскую систему баз данных JSON и включает в себя очень простой язык запросов, который поможет вам сделать именно то, что вы ищете:

// Create a new instance of ForerunnerDB and then ask for a database
var fdb = new ForerunnerDB(),
    db = fdb.db('myTestDatabase'),
    coll;

// Create our new collection (like a MySQL table) and change the default
// primary key from "_id" to "id"
coll = db.collection('myCollection', {primaryKey: 'id'});

// Insert our records into the collection
coll.insert([
    {"name":"my Name","id":12,"type":"car owner"},
    {"name":"my Name2","id":13,"type":"car owner2"},
    {"name":"my Name4","id":14,"type":"car owner3"},
    {"name":"my Name4","id":15,"type":"car owner5"}
]);

// Search the collection for the string "my nam" as a case insensitive
// regular expression - this search will match all records because every
// name field has the text "my Nam" in it
var searchResultArray = coll.find({
    name: /my nam/i
});

console.log(searchResultArray);

/* Outputs
[
    {"name":"my Name","id":12,"type":"car owner"},
    {"name":"my Name2","id":13,"type":"car owner2"},
    {"name":"my Name4","id":14,"type":"car owner3"},
    {"name":"my Name4","id":15,"type":"car owner5"}
]
*/

Отказ от ответственности: я разработчик ForerunnerDB.

Другие вопросы по тегам