How to convert an array of even number of element into key value pair document with key name is the even number and value the odd number

hello i have the following array [“name”,“david”,“age”,“24”,“gender”,“male”] i want to convert it like this
[{“name”:“david”},{“age”:“24”},{“gender”:“24”}] i appreciate your help thanks

Well, I think it could be done more easily in Python than in MQL.
In Python, you can do the following:

tmp_arr = ["name","david","age","24","gender","male"]
key_arr = tmp_arr[::2]  # Extract all the odd-numbered elements from tmp_arr.
val_arr = tmp_arr[1::2]  # Extract all the even-numbered elements from tmp_arr.
tmp_dict = {}

for e in zip(key_arr, val_arr):
    tmp_dict[e[0]] = e[1]

print(tmp_dict)
{'name': 'david', 'age': '24', 'gender': 'male'}

Now you have a dictionary containing three elements.

Here is a solution that provides the result in a format that is almost what you want. It is certainly a little bit more complicated than Zhen_Qu solution but the work is done in the server. Downloading, millions of documents to run a conversion on the client is often impractical and slow.

Starting collection:

{ _id: 1,  a: [ 'name', 'david', 'age', '24', 'gender', 'male' ] }

The solution leverages the fact that mongosh is JS based.

// Using k and v for key and value

// Small steps (using divide and conquer) that come together in the pipeline

// The pipeline will have a multiple $set stages built with
//
_set = function( fields ) {
	return { "$set" : fields }
}

// Mohamed_Habib did not named the array but mine is named a
//
_array = "$a"

// Generates odd and event indexes
// start == 0 for even
// start == 1 for odd
//
_indexes = function(start) {
	return { "$range" : [ start , { "$size" : _array } , 2 ] }
}

// Stage to add indexes of keys
//
_set_k_indexes = _set( { "k_indexes" : _indexes( 0 ) } )

// Stage to add indexes of values
//
_set_v_indexes = _set( { "v_indexes" : _indexes( 1 ) } )

// Extract the element of _array at index
//
_element = { "$arrayElemAt" : [ _array , "$$index"] }

// Simply map indexes to the value in the array
//
_map = function( indexes ) {
	return { "$map" : { "input" : indexes ,
		"as" : "index" ,
		"in" : _element } }
}

// Stage to map index of keys to the key itself
//
_set_k = _set( { "k" : _map( "$k_indexes" ) } )

// Stage to map index of values to the value itself
//
_set_v = _set( { "v" : _map( "$v_indexes" ) } )

// Appreciate the name similarity to the python solution
//
_zip = function( k , v ) {
	return { "$zip" : { "inputs" : [ k , v ] } }
}

// Stage to zip the keys and the values together
//
_set_k_v = _set( { "k_v" : _zip( "$k" , "$v" ) } )

_set_result = _set( {"result" : { "$arrayToObject" : "$k_v" } } )

pipeline = [ _set_k_indexes ,
	_set_v_indexes ,
	_set_k ,
	_set_v ,
	_set_k_v ,
	_set_result ]

/*  Resulting pipeline

[ { '$set': { k_indexes: { '$range': [ 0, { '$size': '$a' }, 2 ] } } },
  { '$set': { v_indexes: { '$range': [ 1, { '$size': '$a' }, 2 ] } } },
  { '$set': 
     { k: 
        { '$map': 
           { input: '$k_indexes',
             as: 'index',
             in: { '$arrayElemAt': [ '$a', '$$index' ] } } } } },
  { '$set': 
     { v: 
        { '$map': 
           { input: '$v_indexes',
             as: 'index',
             in: { '$arrayElemAt': [ '$a', '$$index' ] } } } } },
  { '$set': { k_v: { '$zip': { inputs: [ '$k', '$v' ] } } } },
  { '$set': { result: { '$arrayToObject': '$k_v' } } } ]
*/

/* And running the pipeline on the collection will give:

[ { _id: 1,
    a: [ 'name', 'david', 'age', '24', 'gender', 'male' ],
    k_indexes: [ 0, 2, 4 ],
    v_indexes: [ 1, 3, 5 ],
    k: [ 'name', 'age', 'gender' ],
    v: [ 'david', '24', 'male' ],
    k_v: [ [ 'name', 'david' ], [ 'age', '24' ], [ 'gender', 'male' ] ],
    result: { name: 'david', age: '24', gender: 'male' } } ]
*/

Of course, you should use $project to only keep result but while developing I like to keep intermediate values.

Yes, you can do everything with a single stage pipeline that looks like:

_result = { "$arrayToObject" : _zip( _map( _indexes(0)) , _map(_indexes(1)) ) }
pipeline = [ _set( { "result" : _result } ) ]

/*  Giving (the joyful braces and brackets party)

[ { '$set': 
     { result: 
        { '$arrayToObject': 
           { '$zip': 
              { inputs: 
                 [ { '$map': 
                      { input: { '$range': [ 0, { '$size': '$a' }, 2 ] },
                        as: 'index',
                        in: { '$arrayElemAt': [ '$a', '$$index' ] } } },
                   { '$map': 
                      { input: { '$range': [ 1, { '$size': '$a' }, 2 ] },
                        as: 'index',
                        in: { '$arrayElemAt': [ '$a', '$$index' ] } } } ] } } } } } ]
*/

If your field names are always only name, age and gender and always in this order than this code is way too complicated. But if field names are really dynamic see Building with Patterns: The Attribute Pattern | MongoDB Blog, highly recommended for the extra flexibility and indexability (not a real word but understandable in this context).

1 Like

thank you for your help and time

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.