JSON найти в JavaScript - PullRequest
       37

JSON найти в JavaScript

57 голосов
/ 22 декабря 2009

Есть ли лучший способ, кроме циклического поиска данных в 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 ]

184 голосов
/ 22 декабря 2009

(Вы ищете не через "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 тривиален:

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

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

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

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

delete objJsonResp[id];

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

Вставка карты 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 тривиально:

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 и их методы) помечены как не -enumerable. Но он ломается, как только вы устанавливаете (или задаете фреймворк) любые другие свойства объекта массива (что вполне допустимо; массивы - это просто объекты с небольшой специальной обработкой вокруг свойства length).

8 голосов
/ 25 июля 2012

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

Я не смог найти в сети хороших решений для использования, и использование .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] );
    }
}

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

6 голосов
/ 05 января 2014

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 - это, как вы знаете, стандартизированный язык запросов.

0 голосов
/ 07 декабря 2016

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

0 голосов
/ 22 декабря 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...