web-dev-qa-db-fra.com

JSON trouver en JavaScript

Existe-t-il un meilleur moyen de rechercher des données dans JSON en boucle? C'est pour éditer et supprimer.

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

Les données sont organisées sous forme de liste de cartes . Comme:

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

(Vous ne recherchez pas dans "JSON", mais dans un tableau - la chaîne JSON a déjà été désérialisée dans un graphe d'objet, dans ce cas un tableau.)

Quelques options:

Utiliser un objet au lieu d'un tableau

Si vous contrôlez la génération de cette chose, est-ce que doit être un tableau? Parce que sinon, il existe un moyen beaucoup plus simple.

Dis que ce sont tes données d'origine:

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

Pourriez-vous faire la chose suivante à la place?

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

Il est alors trivial de trouver l'entrée pertinente par ID:

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

... comme le met à jour:

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

... et l'enlever:

delete objJsonResp[id];

Cela tire parti du fait qu'en JavaScript, vous pouvez indexer un objet en utilisant un nom de propriété en tant que chaîne. Cette chaîne peut être littérale ou provenir d'une variable comme avec id ci-dessus.

Mettre dans une carte ID-to-Index

(Idée stupide, antérieure à celle ci-dessus. Conservée pour des raisons historiques.)

Il semble que vous ayez besoin que ce soit un tableau. Dans ce cas, il n’ya pas vraiment de meilleur moyen que de chercher dans le tableau, sauf si vous souhaitez placer une carte dessus, ce que vous pourriez faire si vous avez le contrôle de la génération du fichier. objet. Par exemple, disons que vous avez ceci à l'origine:

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

Le code générateur pourrait fournir une carte 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"}
    ]
}

Puis obtenir une entrée pour l'id dans la variable id est trivial:

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

Cela tire parti du fait que vous pouvez indexer des objets à l'aide de noms de propriétés.

Bien sûr, si vous faites cela, vous devez mettre à jour la carte lorsque vous modifiez le tableau, ce qui pourrait devenir un problème de maintenance.

Mais si vous ne maîtrisez pas la génération de l'objet ou si vous mettez trop de code et/ou de maintenance à mettre à jour la mappe id-to-indexes, vous devrez effectuer une recherche brutale.

Recherche de force brute (corrigé)

Un peu OT (bien que vous avez demandez s'il y avait un meilleur moyen :-)), mais votre code pour la boucle dans un tableau est incorrect. Détails ici , mais vous ne pouvez pas utiliser for..in pour parcourir les index de tableaux (ou plutôt, si vous le faites, vous devez faire tout ce qui est en votre pouvoir); for..in parcourt les propriétés d'un objet _ et non le index d'un tableau_. Votre meilleur pari avec un tableau non clairsemé (et le vôtre non clairsemé) est une boucle classique à l'ancienne:

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

ou

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

Celui que vous préfériez (ce dernier n’est pas toujours plus rapide dans toutes les mises en œuvre, ce qui est contre-intuitif pour moi, mais nous le sommes). (Avec un tableau peu dense, vous pouvez utiliser for..in, mais en prenant de nouveau des efforts particuliers pour éviter les pièges; consultez l'article lié ci-dessus.)

L'utilisation de for..in sur un tableau semble fonctionne dans des cas simples, car les tableaux ont des propriétés pour chacun de leurs index et que leurs seules autres propriétés par défaut (length et leurs méthodes) sont marquées comme non énumérables. Mais il se brise dès que vous définissez (ou un cadre définit) toute autre propriété sur l'objet tableau (ce qui est parfaitement valide; les tableaux ne sont que des objets avec un peu de traitement spécial autour de la propriété length).

182
T.J. Crowder

J'avais rencontré ce problème pour un modèle complexe avec plusieurs objets imbriqués. Voici un bon exemple de ce que je cherchais à faire: Disons que vous avez un polaroïde de vous-même. Et cette image est ensuite placée dans le coffre d'une voiture. La voiture est à l'intérieur d'une grande caisse. La caisse se trouve dans la cale d’un grand navire avec de nombreuses autres caisses. Je devais fouiller la cale, regarder dans les caisses, vérifier le coffre, puis chercher une photo existante de moi.

Je ne trouvais aucune solution en ligne à utiliser, et utiliser .filter() ne fonctionne que sur des tableaux. La plupart des solutions suggéraient simplement de vérifier si model["yourpicture"] existait. Cela était très indésirable car, à partir de l'exemple, cela ne ferait que fouiller la cale du navire et j'avais besoin d'un moyen de les éloigner plus loin du terrier du lapin.

C'est la solution récursive que j'ai faite. Dans les commentaires, j'ai confirmé de T.J. Crowder qu'une version récursive serait nécessaire. Je pensais que je le partagerais au cas où quelqu'un rencontrerait une situation complexe similaire.

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;
}

Cela commencera à partir d'un objet donné à l'intérieur du graphique, et retira tous les objets trouvés. Je l'utilise comme ça:

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

Ce qui produira un tableau des caisses contenant ma photo.

8
Travis J

Zapping - vous pouvez utiliser cette lib javascript; DefiantJS. Il n'est pas nécessaire de restructurer les données JSON en objets pour faciliter la recherche. Au lieu de cela, vous pouvez rechercher la structure JSON avec une expression XPath comme celle-ci:

    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 étend l'objet global JSON avec une nouvelle méthode; "search" qui renvoie le tableau avec les correspondances (tableau vide si aucun n'a été trouvé). Vous pouvez l'essayer vous-même en collant vos données JSON et en testant différentes requêtes XPath ici:

http://www.defiantjs.com/#xpath_evaluator

Comme vous le savez, XPath est un langage de requête standardisé.

6
Hakan Bilgin

Si vous effectuez cette opération à plusieurs endroits de votre application, il serait logique d'utiliser une base de données JSON côté client, car la création de fonctions de recherche personnalisées est complexe et moins gérable que la solution de remplacement.

Découvrez ForerunnerDB qui vous fournit un système de base de données JSON côté client très puissant et comprend un langage de requête très simple pour vous aider à faire exactement ce que vous recherchez:

// 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"}
]
*/

Disclaimer: Je suis le développeur de ForerunnerDB.

0
Rob Evans

Si les données JSON de votre tableau sont triées d'une manière ou d'une autre, vous pouvez effectuer différentes recherches. Cependant, si vous ne gérez pas beaucoup de données, l'opération O(n) sera probablement satisfaisante. Tout le reste serait probablement exagéré.

0
Justin Swartsel