67 votes

Recherche JSON en JavaScript

Existe-t-il un meilleur moyen que le bouclage pour trouver des données dans le fichier JSON ? C'est pour modifier 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,...},
  ...
]

0 votes

Quelqu'un a-t-il remarqué que les suppressions ne modifient pas la longueur et que la position de l'élément @supprimé peut rester indéfinie (aucune réindexation n'est effectuée) à moins qu'il ne s'agisse du dernier élément, la diminution de la longueur pour corriger cela supprime le dernier élément ? Splice semble les mettre à jour.

193voto

T.J. Crowder Points 285826

(Vous ne cherchez pas dans "JSON", vous cherchez dans un tableau -- la chaîne JSON a déjà été désérialisée en un graphe d'objets, 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 ont pour être un tableau ? Parce que si ce n'est pas le cas, il y a un moyen beaucoup plus simple.

Disons que ce sont vos données originales :

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

Pourriez-vous plutôt faire ce qui suit ?

{
    "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];

...tout comme sa mise à jour :

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

...et de l'enlever :

delete objJsonResp[id];

Cette méthode tire parti du fait qu'en JavaScript, vous pouvez indexer un objet en utilisant le nom d'une propriété sous la forme d'une chaîne de caractères - et cette chaîne peut être littérale ou provenir d'une variable, comme dans l'exemple suivant id ci-dessus.

Mise en place d'une carte ID-to-Index

(Idée stupide, antérieure à ce qui précède. Conservée pour des raisons historiques).

Il semble que vous ayez besoin que ce soit un tableau, auquel cas il n'y a pas vraiment de meilleur moyen que de chercher dans le tableau, à moins que vous ne vouliez mettre une carte dessus, ce que vous pourriez faire si vous avez le contrôle de la génération de l'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 de génération pourrait fournir une carte d'identification à l'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];

Cette méthode tire parti du fait que vous pouvez indexer les objets en utilisant les noms des 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 contrôlez pas la génération de l'objet, ou si la mise à jour de la carte des identifiants et des index représente trop de code et/ou un problème de maintenance, vous devrez alors effectuer une recherche par force brute.

Recherche par force brute (corrigée)

Un peu OT (bien que vous a fait demander s'il y avait un meilleur moyen :-) ), mais votre code pour boucler dans un tableau est incorrect. Détails ici mais vous ne pouvez pas utiliser for..in pour boucler les index des tableaux (ou plutôt, si vous le faites, vous devez prendre des précautions particulières pour le faire) ; for..in boucles à travers le les propriétés d'un objet et non le indices d'un tableau . Votre meilleure chance avec un tableau non épars (et le vôtre est non épars) est une bonne vieille boucle standard :

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

ou

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

Peu importe ce que vous préférez (cette dernière solution n'est pas toujours plus rapide dans toutes les implémentations, ce qui est contre-intuitif pour moi, mais nous y sommes). (Avec un éparses vous pouvez utiliser for..in mais en prenant soin d'éviter les pièges ; voir l'article ci-dessus).

Utilisation de for..in sur un tableau semble pour fonctionner dans des cas simples car les tableaux ont des propriétés pour chacun de leurs index, et leurs seules autres propriétés par défaut ( length et leurs méthodes) sont marqués comme non dénombrables. Mais cela s'arrête dès que vous définissez (ou qu'un framework définit) d'autres propriétés sur l'objet tableau (ce qui est parfaitement valide ; les tableaux sont juste des objets avec une gestion un peu spéciale autour de la balise length propriété).

0 votes

Thx, je vais prendre la première solution qui me semble la meilleure.

0 votes

Excellent hack : for(var i=someArray.length;0<--i ;){/* ... */}

2 votes

@TJCrowder - Cela ne fonctionne pas pour un véritable graphe d'objets. Vous ne couvrez qu'une seule couche (le haut). Si ce modèle était plus complexe, alors une valeur imbriquée ne serait pas trouvée.

8voto

Travis J Points 28588

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

Je n'ai pas pu trouver de bonnes solutions en ligne à utiliser, et en utilisant .filter() ne fonctionne que sur les tableaux. La plupart des solutions suggéraient de simplement vérifier si model["yourpicture"] existé. Ce n'était pas du tout souhaitable parce que, d'après l'exemple, cela n'aurait permis de chercher que dans la cale du navire et j'avais besoin d'un moyen de les trouver plus loin dans le 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. J'ai pensé que je la 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;
}

Elle partira d'un objet donné dans le graphe, et fera une récursion dans tous les objets trouvés. Je l'utilise comme ceci :

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 qui contiennent mon image.

8voto

Hakan Bilgin Points 346

Zapping - vous pouvez utiliser cette librairie 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 retourne un tableau avec les correspondances (tableau vide si aucune n'a été trouvée). 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

XPath est, comme vous le savez, un langage d'interrogation standardisé.

0 votes

Merci, je n'oublierai pas de l'essayer la prochaine fois.

0voto

Rob Evans Points 1380

Si vous effectuez cette opération à plusieurs endroits dans votre application, il serait judicieux d'utiliser une base de données JSON côté client, car la création de fonctions de recherche personnalisées est compliquée et moins facile à maintenir que l'autre solution.

Découvrez ForerunnerDB, qui offre un système de base de données JSON côté client très puissant et comprend un langage d'interrogation 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"}
]
*/

Clause de non-responsabilité : je suis le développeur de ForerunnerDB.

0voto

C. Graham Points 179

Ok. Alors, je sais que c'est un vieux post, mais peut-être que cela peut aider quelqu'un d'autre. Ce n'est pas rétrocompatible, mais c'est presque sans importance puisque Internet Explorer est en train de devenir superflu.

Le moyen le plus simple de faire exactement ce que l'on veut :

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

Elle renverra l'élément que vous voulez modifier si vous fournissez 'edit' comme type. Si vous fournissez autre chose, ou rien, elle supposera que l'élément est supprimé. Vous pouvez inverser les conditionnels si vous le souhaitez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X