Searching json object key containing value, then unset it

后端 未结 1 1844
陌清茗
陌清茗 2021-01-24 03:31

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 e

相关标签:
1条回答
  • 2021-01-24 04:26

    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.

    0 讨论(0)
提交回复
热议问题