wankdanker / node-object-mapper

Copy properties from one object to another.
MIT License
277 stars 73 forks source link

Added support for Arrays mapping #1

Closed dearwish closed 9 years ago

dearwish commented 11 years ago

Hello Dan,

I am using the object-mapper module in my project and find it very useful, but it is missing some basic support for Arrays mapping which in my case is a must. Therefore I added this support and updated the test case accordingly to show the new functionality in action. Please consider pulling my changes to your repository and push them to npm to avoid duplicate npm repositories with different versions.

Thank you in advance, David

wankdanker commented 11 years ago

Hi David,

I am glad you find this module useful. I also recently came across a case where I needed support for Arrays. I had made the necessary changes but never submitted them to github. After a quick review of your code, it looks like what you have done may be better than what I did. I will have to look at this more in depth, hopefully on Monday.

In the mean time, I published my changes to: https://github.com/wankdanker/node-object-mapper/tree/array-support in case you would like to review what I did. Unfortunately, I did not update the readme.

Once we figure out which way to go with this, I will definitely push v0.0.5 to npm with array support.

Thank you for your work.

Dan

dearwish commented 11 years ago

Hi Dan,

I am considering enhancing the mapping functionality with more features like:

  1. Adding array index to the result object (i.e. when the mapping is defined as {"arr[seq]", {key: "names[seq], index:true"}} , the result for input [{name: 'Dan'}, {name: 'David'}] will contain this index: [{name: 'Dan', seq: 1}, {name: 'David', seq: 2] ).
  2. Mapping from array to other type like String, Number, Object using the transform function.

What you think?

Thank you, David

wankdanker commented 11 years ago

Hi David,

  1. I see how the index could be useful. I wonder if specifying it like this would be better:
map = { 
    "arr[i]" : { 
        key : "names[i]",
        , index : "seq"
    }
}

/* with */

data = {
    arr : [
        {name: 'Dan'}
        , {name: 'David'}
    ]
};

/* would result in */

[
    {name: 'Dan', seq: 1}
    , {name: 'David', seq: 2}
]

That way, if index is truthy we can just use its value as the name of the index attribute on the target object. This would allow us to use what is inside the '[]' for something else in the future.

  1. Could you provide an example of mapping from an array to another type to help me understand what you are thinking?

Thank you,

Dan

dearwish commented 11 years ago

Hi Dan,

  1. You are right, this way is more convenient.
  2. Example can be to transform array to concatenated string with delimiters, sum of all array values, or array with transformed objects. I am working with comb library and it has many useful array functions that can be reused here. Take a look here: http://c2fo.github.com/comb/comb_array.html
// Map array to string
map = { 
    "arr[i].tag" : { 
        key: "tags"
        , delimiter: ","
        , transform: function (value, objFrom, objTo) {
            return value;
        }
    }
}

/* with */

data = {
    arr: [
        {tag: 'name'}
        , {tag: 'title'}
    ]
};

/* would result in */

{
    tags: "name,title"
}
dearwish commented 11 years ago

Hello Dan,

During my project development I found that I am also missing some basic support for constants. For example if I need to set a record.Type field with constant value "user" in result object, currently I cannot do this. It would be nice if the mapper will be able to determine by some syntax that some mapping entry is actually a constant value for the target field. e.g.


// Map constants
map = { 
    "name" : "user.Name", 
    "\"user\"" : "user.Type"
    }
}

/* with */

data = {
    name: "David"
};

/* would result in */

{
    user: {
        Name: "David",
        Type: "user"
    }
}

What you think?

Thank you, David

wankdanker commented 11 years ago

For those constant cases, I have used the default option like:

map = { 
    fakeSourceKeyName : { 
        key : "user.Type",
        , default : "user"
    }
}

or the array specification which is a little bit more concise:

map = { 
    fakeSourceKeyName : ["user.Type", null, "user"]
}

But, I think what you have presented is a nice option because it is the MOST concise. I think it is a good idea. :)

I'm sorry I haven't gotten back to you about the other requests yet. I have been very busy with other things. :-/

One feature that I have in my fork, that is available in the array-support branch, is multidimensional array processing. I'm not sure if you have taken a look at that, but I would like you input on that.

I have encountered cases where some source array contains objects which themselves contain arrays. I need a way to address those and map them to whatever I want. I have a rudimentary implementation working in the branch I mentioned, but it's not perfect. If you have any thoughts on that, I'm all ears.

Thank you,

Dan

cgarvis commented 11 years ago

Has this been merged into master yet?

wankdanker commented 11 years ago

@cgarvis, If I recall correctly, no it hasn't.

wankdanker commented 9 years ago

@rafakato added array mapping which has been released in v2.0.0. If it doesn't work as needed/expected please open another issue.

Thanks!

seba-i commented 6 years ago

Hi, what about array mapping WITH constants, I can't get this to work?

// array with constant
let obj1 = [{'a' : 11}]
let map1 = {
  '[].a' : '[].b', // maps correctly
  '[].notExistingKey': [['[].c', null, 'STATIC']] // ** does not map correctly **
}
let result2 = objectMapper(obj1, map1) //-> [{'b':11}]  missing field 'c'

// object with constant does work as expected
let obj2 = {'a' : 11}
let map2 = {
  'a' : 'b', // maps correctly
  'notExistingKey': [['c', null, 'STATIC']] // maps correctly
}
let result1 = objectMapper(obj2, map2) //-> {'b':11,'c':'STATIC'}  correct

Tried a few notations but couldn't get it to work... Thanks.