admin管理员组

文章数量:1318564

I need mongodb's $unwind like functionality with underscore, so that

[{
    groups: [{ name: 'test 1' }, { name: 'test 2' }],
    id: 1
}]

became

[{
    id: 1,
    group: {
        name: 'test 1'
    }
}, {
    id: 1,
    group: {
        name: 'test 2'
    }
}]

Can this be done with underscore?

I need mongodb's $unwind like functionality with underscore, so that

[{
    groups: [{ name: 'test 1' }, { name: 'test 2' }],
    id: 1
}]

became

[{
    id: 1,
    group: {
        name: 'test 1'
    }
}, {
    id: 1,
    group: {
        name: 'test 2'
    }
}]

Can this be done with underscore?

Share Improve this question asked Sep 25, 2014 at 11:31 karaxunakaraxuna 26.9k13 gold badges86 silver badges119 bronze badges
Add a ment  | 

7 Answers 7

Reset to default 5

You can map a new object for each element in the target field. For example, with a mixin and assuming you want a method signature looking like _.unwind(object, field) :

_.mixin({
    unwind: function(o, field) {
        return _.map(o[field], function(val) {
            var cloned = _.clone(o);
            cloned[field] = val;
            return cloned;
        });
    }
});

You would use it like this :

_.unwind({
    groups: [{ name: 'test 1' }, { name: 'test 2' }],
    id: 1
}, 'groups');

And

  • a Fiddle http://jsfiddle/nikoshr/ag31ypeb/
  • a snippet

function log(obj) {
  document.getElementById('logged').innerHTML+= '<p>'+JSON.stringify(obj)+'</p>';
}

_.mixin({
    unwind: function(o, field) {
        return _.map(o[field], function(val) {
            var cloned = _.clone(o);
            cloned[field] = val;
            return cloned;
        });
    }
});

var o = { _id : 1, item : "ABC1", sizes: [ "S", "M", "L"] };
log(_.unwind(o, 'sizes'));

var g = {
    groups: [{ name: 'test 1' }, { name: 'test 2' }],
    id: 1
};
log(_.unwind(g, 'groups'));
<script src="http://cdnjs.cloudflare./ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>

<div id='logged'></div>

If you need unwind an array of objects (more like mongodb's unwind) you can use this function:

function unwindBy(arr, field){
  return _.transform(arr, function(r, o){ 
    Array.prototype.push.apply(r, 
      _.map(o[field], function(v){ return _.set(_.clone(o), field, v); })
    );
  }, []);
}

ES6 allows rewrite it much shorter and with no dependencies to 3rd party libs:

function unwindBy(arr, f) {
  return arr.reduce((r, o) => r.concat(o[f].map(v => ({ ...o, [f]: v }))), []);
}

Example:

unwindBy([{a:1, b:[1,2,3]}, {a:2, b:[3,4,5]}], 'b');
//[{a:1, b:1}, {a:1, b:2}, {a:1, b:3}, {a:2, b:3}, {a:2, b:4}, {a:2, b:5}]

In case you want to add the destination field, you can do it using the below snippet. It is a modification based on @nikoshr's answer.

function log(obj) {
  document.getElementById('logged').innerHTML+= '<p>'+JSON.stringify(obj)+'</p>';
}

_.mixin({
    unwind: function(o, field, toField) {
        if(!toField) toField = field;
        return _.map(o[field], function(val) {
            var cloned = _.clone(o);
            cloned[toField] = val;
            return cloned;
        });
    }
});

var o = { _id : 1, item : "ABC1", sizes: [ "S", "M", "L"] };
log(_.unwind(o, 'sizes'));

var g = {
    groups: [{ name: 'test 1' }, { name: 'test 2' }],
    id: 1
};
log(_.unwind(g, 'groups', 'group'));
<script src="http://cdnjs.cloudflare./ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>

<div id='logged'></div>

This function will help to unwind a json object.

function flatten()
{
    var objectData = [];
    var arrayData = [];
    var arrayDataKey = [];
    var resultSet = [];

    this.progress = function(doc){
        objectData = [this.getObjectData(doc)];
        this.unwindData(objectData,arrayData);
        return resultSet;
    }

    this.getObjectData = function(doc,keyId){ 
        var result = {};
        var index = '';
        if(keyId=='' || keyId==undefined){
            keyId = '';
        }

        for(key in doc){
            if(keyId==''){
                index = key;
            }else{    
                index =  keyId+'.'+key;
            }

            if(Array.isArray(doc[key])===true){
                if(typeof doc[key][0]=="object"){
                    arrayDataKey.push(key);
                    arrayData.push(doc[key]);
                }else{
                    result[index] = doc[key];
                }
            }else if(typeof doc[key]=="object"){
                if(doc[key]=="[object Object]"){
                    for (var k in data = this.getObjectData(doc[key],index)) { result[k] = data[k]; }
                }else{

                    result[index] = doc[key];
                }
            }else{
                result[index] = doc[key];
            }
        }
        return result;
    }

    this.unwindData = function(doc,objectData){
        for (var i = objectData.length - 1; i >= 0; i--) {
            resultSet = [];
            for(var key in doc){
                for (var prop in objectData[i]) {
                    objectData[i][prop] = this.getObjectData(objectData[i][prop]);
                    for(var k in objectData[i][prop]){
                        var ke = arrayDataKey[i]+'.'+k;
                        doc[key][ke] = objectData[i][prop][k];
                    }
                    resultSet.push(doc[key]);
                }
            }
            doc  = resultSet;
        }
    }
}

var flat = new flatten(); flat.progress();

You can check out lodash-unwind

Sample usage:

const _ = require('lodash')
require('lodash-unwind')({ injected: true })
const data = [
{
    a: [ 1, 2 ],
    id: 'a1'
},
{
    a: [ 3, 4 ],
    id: 'a2'
}]
// Use unwind as part of lodash
const output = _.unwind(data, 'a')
// [
//   {
//     a: 1,
//     id: 'a1'
//   },
//   {
//     a: 2,
//     id: 'a1'
//   },
//   {
//     a: 3,
//     id: 'a2'
//   },
//   {
//     a: 4,
//     id: 'a2'
//   }
// ]

You can try to use npm package unwind-array see some example usages.

There is npm package unwind-array that you can try.

const { unwind } = require('unwind-array')
const result = unwind(
  {
    title: 'foobar',
    topLevelArr: [1, 2]
  },
  { path: 'topLevelArr' }
)
expect(result.length).to.be.equal(2)
expect(result).to.be.deep.equal([
  {
    title: 'foobar',
    topLevelArr: 1
  },
  {
    title: 'foobar',
    topLevelArr: 2
  }
])

本文标签: javascriptUnwind functionality with underscoreStack Overflow