macek/jquery-serialize-object

ignore empty field in serialization?

antonioaltamura opened this issue · 6 comments

Hi, there is a way to not serialize empty fields?

macek commented

This is sort of a complicated problem and not really within the scope of this plugin. You issue doesn't provide a lot to go off of, but it directly conflicts with #38 which I know to be the behavior of jQuery.serializeArray. So my best guess is that you're trying to remove things like empty Objects or empty Arrays?

// helper function for determining value's type
const type = x => {
  if (x === undefined) return undefined;
  if (x === null) return null;
  return x.constructor;
};

// define which values are to be considered "empty"
const isEmpty = x => {
  switch (type(x)) {
    case undefined: return true;
    case null: return true;
    case Object: return isEmpty(Object.keys(x));
    case Array: return x.length === 0;
    case String: return x.length === 0;
  }
};

// removeEmptyValues(obj) - deeply remove all "empty" values
const removeEmptyValues = x =>
  Object.keys(x).reduce((y, k) => {
    if (isEmpty(x[k])) return y;
    if (type(x[k]) === Object) return Object.assign(y, {[k]: removeEmptyValues(x[k])});
    return Object.assign(y, {[k]: x[k]});
  }, {});

To see this work, I'll create some fake input

var input = {
  someString: 'yes',
  someNumber: 0,
  someBool: true,
  someArray: [1,2,3],
  emptyString: '',
  emptyObject: {},
  emptyArray: [],
  someNull: null,
  someUndefined: undefined,
  someObject: {
    someNestedString: 'hello',
    someNestedNumber: 2,
    someNestedArray: [1],
    nestedEmptyString: '',
    nestedEmptyObject: {},
    nestedEmptyArray: [],
    deeply: {nested: {emptyObject: {}}}
  }
};

// remove the empty values
var output = removeEmptyValues(input);
console.log(output);

Output

{
  "someString": "yes",
  "someNumber": 0,
  "someBool": true,
  "someArray": [
    1,
    2,
    3
  ],
  "someObject": {
    "someNestedString": "hello",
    "someNestedNumber": 2,
    "someNestedArray": [
      1
    ],
    "deeply": {
      "nested": {}
    }
  }
}

As you can see, it gets hairy pretty quickly, and this really isn't the responsibility of this plugin. This type of functionality should probably be packaged into its own plugin.

Anyway, this cleans up the majority of the "empty" values. The only bug here is it's pretty tricky to remove the {someObject: {deeply: {nested: {emptyObject: {}}}}. Not sure how to fix that in any elegant manner right now.

So how would you use this with jquery-serialize-object?

// include the code above
var data = $('#form').serializeObject();
var json = JSON.stringify(removeEmptyValues(data));

Good luck

wow, that's what I call support.
I'm developing a very basic Rest API in node.js.
The "select" query on my db will be sent via POST as json object, and those empty value simply stands for "select the documents without that field" obiouvsly I don't want this behaviour: an empty field should match every values.

I will use very soon (I hope without any kind of problem, but I'm sure something will come up )

Very thanks!! 👍

Hi, sorry to reopen, can we translate that Object.assign in ES5?

macek commented

Sure. Here's one way you could write it

if (Object.assign === undefined) {
  Object.assign = function assign(x) {
    function _assign(x, y) {
      return Object.keys(y).reduce(function(z, k) {
        z[k] = y[k];
        return z;
      }, x);
    }
    return Array.prototype.slice.call(arguments, 1).map(Object).reduce(_assign, x);
  };
}

Or there's an Object.assign polyfill available on the MDN wiki

if (typeof Object.assign != 'function') {
  (function () {
    Object.assign = function (target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }

      var output = Object(target);
      for (var index = 1; index < arguments.length; index++) {
        var source = arguments[index];
        if (source !== undefined && source !== null) {
          for (var nextKey in source) {
            if (source.hasOwnProperty(nextKey)) {
              output[nextKey] = source[nextKey];
            }
          }
        }
      }
      return output;
    };
  })();
}

i will try as soon as possible, very thanks man, I really mean it.

Ok I tried to translate in ES5 obiouvsly I'm not really into ES6 to understand how you use assign() :/

function type (x) {
    if (x === undefined) return undefined;
    if (x === null) return null;
    return x.constructor;
}

// define which values are to be considered "empty"
function isEmpty(x) {
    switch (type(x)) {
        case undefined: return true;
        case null: return true;
        case Object: return isEmpty(Object.keys(x));
        case Array: return x.length === 0;
        case String: return x.length === 0;
    }
};

// removeEmptyValues(obj) - deeply remove all "empty" values
function removeEmptyValues (x){
    Object.keys(x).reduce(function(y, k) {
        if (isEmpty(x[k])) return y;
        if (type(x[k]) === Object) return Object.assign(y, removeEmptyValues(x[k]));
        return Object.assign(y,x[k]);
});
}

if (Object.assign === undefined) {
    Object.assign = function assign(x) {
        function _assign(x, y) {
            return Object.keys(y).reduce(function(z, k) {
                z[k] = y[k];
                return z;
            }, x);
        }
        return Array.prototype.slice.call(arguments, 1).map(Object).reduce(_assign, x);
    };
}