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,...},
  ...
]

Ответ 1

(Вы не просматриваете "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": "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 в переменной id тривиально:

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

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

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

Но если вы не контролируете создание объекта или обновляете карту идентификаторов-индексов, слишком много проблем с обслуживанием кода и/или, то вам придется выполнять поиск грубой силы.

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

Немного 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).

Ответ 2

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

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

Это рекурсивное решение, которое я сделал. В комментариях я подтвердил от T.J. 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] );
    }
}

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

Ответ 3

Zapping - вы можете использовать эту javascript lib; 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_evaluator

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

Ответ 4

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

Ответ 5

Если вы делаете это более чем в одном месте в своем приложении, имеет смысл использовать базу данных 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.