Searching json object key containing value, then unset it

依然范特西╮ 提交于 2019-12-02 11:58:51

问题


I'm looking for a way to search a JSON object to check if it contains a given value, and if it exists, then unset it.

My data is structured as follows (commented with explanation):

// Search within the 'seats' array for a given 'guestID', if it exists, unset it
tables = [
   {
      "_id":{
         $oid: "one"
      },
      "seats":[
         { "guestId":"01" },
         { "guestId":"02" },
         { "guestId":"03" }
      ]
   },
   {
      "_id":{
         $oid: "two"
      },
      "seats":[
         { "guestId":"11" },
         { "guestId":"12" },
         { "guestId":"13" }
      ]
   }
]

I am using underscore for this project, and I've tried using _.pluck(tables, 'seats') and then _.foreach but I was having to nest multiple _.foreach statements to access the seats array for searching, and I'm not sure if that's a best practice or not. Is there an easier way that I'm not aware of?

This data is being returned from the mongolab REST api. Is this something I should be doing in my initial XHR request, rather than getting a large object and then trying to parse through it client-side?

If this were an SQL request I'd just be able to do something like select tables.seats where guestId = XXX


回答1:


Whenever I've come across these kinds of situations in the past, a recursive search function has always been invaluable... here's one of mine I had lying around (I extended it by adding a remove method):

function o ( target ) {
  /// if an instance of o has been created, then handle it
  if ( this instanceof o ) {
    /// O INSTANCE:
    /// an instance of o acts like a reference or pointer 
    /// to how the value it targets was reached.
    this.value = target;
    this.key = arguments[1];
    this.parent = arguments[2];
    this.toString = function(){ return 'o('+this.key+' = '+this.value+')';}
  }
  /// if no instance being created fall back to the 'scan object' code
  else {
    /// RECURSIVE CODE:
    /// the _ function is responsible for accepting the 
    /// attributeName and attributeValue search
    var _ = function ( key, value, modifier ) {
      var i, v, tar = ( modifier ? modifier.target : target ), items = [];
      /// if we are dealing with an o instance, handle slightly differently
      if ( tar instanceof o ) {
        for ( i in tar.value ) {
          /// check to see if our current key and value 
          /// match our search terms
          if ( _.test( i, (v=tar.value[i]), key, value ) ) {
            items.push(new o(v, i, tar));
          };
        };
      }
      /// if no o instance treat as a normal object or array
      else {
        for ( i in tar ) {
          if ( (v = tar[i]) ) {
            /// if we are an instance of o, recurse to actually 
            /// check the items within
            if ( v instanceof o ) {
              items = items.concat( _( key, value, {target:v} ) );
            }
            /// check to see if our current key and value match 
            /// our search terms
            else if ( _.test( i, v, key, value ) ) {
              items.push(new o(v, i, tar));
            };
          };
        };
      };
      /// if in modifier mode, don't rely on storing in scope, 
      /// return the calculated items instead
      if ( modifier ) {
        return items;
      }
      else {
        /// update what we are targeting
        target = items;
        /// return our underscore function
        return _;
      };
    };
    /// FUNCTION DECLARATIONS:
    /// a quick test to see if the key and value match (or either or)
    _.test = function ( i,v,key,value ) {
      var havekey = ( key !== null && key !== undefined ),
          haveval = ( value !== null && value !== undefined ),
          passkey = ( havekey && (i == key || key === '*') ),
          passval = ( haveval && (v == value || value === '*') );
      return ( havekey && haveval && passkey && passval ) || 
        ( havekey && !haveval && passkey ) || 
        ( haveval && !havekey && passval );
    };
    /// calculate the path needed to reach the object within the structure
    _.path = function () {
      var i = target.length, paths = [], path, cur, last;
      while ( i-- ) {
        cur = target[i]; path = [];
        do{ last = cur; if ( cur instanceof o ){ path.unshift( cur.key ); } }
          while( (cur = cur.parent) );
        paths.push(path.join('/'));
      };
      return ( paths.length == 1 ? paths[0] : paths );
    };
    /// remove the item we are targeting by stepping back 
    /// and deleting ourselves from the previous parent
    _.remove = function ( removeEntireObject ) {
      var i = target.length, paths, path, cur, last;
      while ( i-- ) {
        cur = target[i];
        /// remove the object that has the found attribute
        if ( removeEntireObject ) {
          if ( cur.parent.parent ) {
            cur.parent.parent.value[cur.parent.key] = null;
            delete cur.parent.parent.value[cur.parent.key];
          }
        }
        /// otherwise remove only the targeted attribute
        else {
          cur.parent.value[cur.key] = null;
          delete cur.parent.value[cur.key];
        }
      };
      return _;
    };
    /// a useful function for backwards navigation
    _.parent = function () {
      var i = target.length, cur, items = [], values = [];
      while ( i-- ) {
        cur = target[i];
        /// remove the object that has the found attribute
        if ( cur && cur.parent ) {
          /// store the values as we go so we can 
          /// spot and remove duplicated parents
          if ( values.indexOf(cur.parent.value) === -1 ) {
            items.push(cur.parent);
            values.push(cur.parent.value);
          }
        }
      };
      target = items;
      return _;
    }
    /// slimple debugging
    _.alert = function () {
      var i = target.length, cur;
      while ( i-- ) {
        cur = target[i];
        alert(cur);
      };
      return _;
    };
    return _;
  };
};

Example usage:

/// remove only the guestId object with a value '01'

o(tables)('*')('seats')('*')('guestId', '01').remove( true );

or:

/// remove the 'guestIds' object in the first slot for either seat

o(tables)('*')('seats')(0)('guestId', '*').parent().remove();

or:

/// remove all 'guestIds' from the first seat

o(tables)(0)('seats')('*')('guestId').parent().remove();

Explanation:

  • You must always start by calling o(my_object_to_parse).
  • Passing one parameter acts as an attributeName search.
  • Passing two parameters acts as an attributeName and attributeValue search.
  • The * acts as a simple wildcard which can be useful to handle basic arrays.
  • The * can be used as the attributeName or attributeValue.
  • Each successive request moves one level further into the structure.

I haven't used this code in a while, so it may not be 100% bug free or optimal. However it seems to work for your particular use case... and it also managed to handle everything I threw at it whilst testing. It should be easy enough to extend with more methods beyond .remove(), .parent(), .path() and .alert() and it might be best to add in some error checking.



来源:https://stackoverflow.com/questions/12163160/searching-json-object-key-containing-value-then-unset-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!