0
[{
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Confirm": 0
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Expired": 25
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Pending": 390
}, {
    "_id": {
        "year": 2017,
        "month": 5
    },
    "Pending": 1400
}]

The array above contain same value month and year. Generated from MongoDB Aggregate. And I want to merge them into a single object and preserve whatever keys and values they have.

Expected output:

[{
    month: 4,
    year: 2017,
    Expired: 25,
    Pending: 390
}, {
    month: 5,
    year: 2017,
    Pending: 1400
}]

I prefer the fastest execution implementation. Underscorejs or native are welcome. Thanks

5
  • How do you want to merge them? Provide your expected output Commented Jun 11, 2017 at 2:20
  • There are keys that match yet not the entire object...do they merge as one object as well? Commented Jun 11, 2017 at 2:28
  • yes @zer00ne. Do I have to introduce another variable Commented Jun 11, 2017 at 2:30
  • So "Confirm": 0 should be in the first object of result as well? Commented Jun 11, 2017 at 2:30
  • It doesn't matter. The order of keys are not my concern Commented Jun 11, 2017 at 2:32

3 Answers 3

1

This takes a little to pick apart, but it is linear:

const ary = [{
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Confirm": 0
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Expired": 25
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Pending": 390
}, {
    "_id": {
        "year": 2017,
        "month": 5
    },
    "Pending": 1400
}];

const result = Object.values(ary.reduce((acc, cur) => {
    const { month, year } = cur._id;
    const key = `${month}-${year}`;
    const obj = Object.assign({}, cur);
    delete obj._id;
    acc[key] = Object.assign(acc[key] || { month, year }, obj);
    return acc;
}, {}));

console.log(result);
Sign up to request clarification or add additional context in comments.

9 Comments

@DanielPérez, no sorting is needed when you use keys.
I guess it is not necessary with mongodb queries, in that case the question should be updated, add mongodb tag , etc
@DanielPérez, it has nothing to do with mongodb. This is how JS objects work.
Run it and ask about what you don't understand :)
I ran it and it throws an error, const result = Object.values(ary.reduce((acc, cur) => { ^ TypeError: Object.values is not a function
|
0

You could use a Map for grouping, and then Array.from to extract the final objects:

function merge(data) {
    return Array.from(data.reduce( (acc, o) => {
        const k = o._id.year * 100 + o._id.month;
        const v = acc.get(k) || Object.assign({}, o._id);
        for (let prop in o) {
            if (prop !== '_id') v[prop] = o[prop];
        }            
        return acc.set(k, v);
    }, new Map), ([k, v]) => v);
}
// Sample data
const data = [{
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Confirm": 0
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Expired": 25
}, {
    "_id": {
        "year": 2017,
        "month": 4
    },
    "Pending": 390
}, {
    "_id": {
        "year": 2017,
        "month": 5
    },
    "Pending": 1400
}];

const result = merge(data);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

This runs in O(N*logN) for sorting and O(N) for merging the json. Hope this works for you!

var obj = [{
    _id: {
        year: 2017,
        month: 5,
    },
    Pending: 1400,
}, {
    _id: {
        year: 2017,
        month: 4,
    },
    Expired: 25,
}, {
    _id: {
        year: 2017,
        month: 4,
    },
    Pending: 390,
}, {
    _id: {
        year: 2017,
        month: 4,
    },
    Confirm: 0,
}];

function compare(a, b) {
    return a._id.year !== b._id.year
        ? a._id.year - b._id.year
        : a._id.month - b._id.month;
}

var sorted = obj.sort(compare);

function join(a, b) {
    return {
        _id: a._id,
        Pending: (a.Pending? a.Pending : 0) + (b.Pending? b.Pending : 0),
        Confirm: (a.Confirm? a.Confirm : 0) + (b.Confirm? b.Confirm : 0),
        Expired: (a.Expired? a.Expired : 0) + (b.Expired? b.Expired : 0),
    };
}

var compressed = sorted.filter(function (value, index) {
    if (!sorted[index + 1]) {
        return true;
    }
    if (compare(value, sorted[index + 1]) === 0) {
        sorted[index + 1] = join(value, sorted[index + 1]);
        return false;
    }
    return true;
});

console.log(compressed);

// if you want month and year formatted:

console.log(compressed.map(function (o) {
    const result = {
        month: o._id.month,
        year: o._id.year,
    };
    if (o.Pending !== undefined) result.Pending = o.Pending;
    if (o.Confirm !== undefined) result.Confirm = o.Confirm;
    if (o.Expired !== undefined) result.Expired = o.Expired;
    return result;
}));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.