33

I have an array of elements that I would like to apply a mapping to to convert it into key value pairs on a single object (to mimic an associative array).

The approach in Can destructuring assignment be used to effect a projection in CoffeeScript? does not seem to work for me as it results in a simple array instead of key/value pairs.

My language of choice is CoffeeScript or JavaScript.

An example:

[{name: 'a', value: 'b', other: 'c'}, {name: 'd', value: 'e', other: 'f'}]

is supposed to be transformed into:

{
  a: 'b',
  d: 'e'
}

One-liners are preferred. ;-)

5
  • Suggest you make an example in pure coffeescript without the jquery dependency; something that can be tested on it's own. If you then do not figure it out, maybe the rest of us can. Commented Dec 1, 2011 at 21:41
  • Marius Kjeldahl: Done. Hope this is a little clearer. Commented Dec 1, 2011 at 21:47
  • 2
    "One-liners are preferred"? Really? # of lines !== quality. Commented Dec 1, 2011 at 22:30
  • 1
    Matt McDonald: Agreed. Still, if there is a clear one-liner I tend to use that one instead of a multi-statement solution. IMHO, the reduce approach qualifies (even though it requires a utility method). Commented Dec 1, 2011 at 23:00
  • tokland: Not yet, but I would not rule it out. Commented Dec 2, 2011 at 10:57

7 Answers 7

80
var arr = [{name: 'a', value: 'b', other: 'c'}, {name: 'd', value: 'e', other: 'f'}];

var obj = arr.reduce(function ( total, current ) {
    total[ current.name ] = current.value;
    return total;
}, {});

Pure javascript. It's practically a one liner, and it looks hawt.

Array.prototype.reduce is ES5, but isn't difficult to shim. Here's an example shim:

Array.prototype.reduce = function ( fun, initVal ) {
    var sum = initVal || this[ 0 ],
        i = 1, len = this.length;

    do {
        sum = fun.call( undefined, sum, this[i], i, this );
    } while ( ++i < len );

    return sum;
};

arr.reduce is a sophisticated version of arr.map, which is a sophisticated version of arr.forEach. You can do this for the same effect:

var obj = {};
arr.forEach(function ( val ) {
    obj[ val.name ] = val.value;
});

//and using jQuery.each
var obj = {};
$.each( arr, function ( index, val ) {
    obj[ val.name ] = val.value;
});

//latter version in coffeescript:
obj = {}
$.each( arr, (index, val) ->
    obj[ val.name ] = val.value
)
Sign up to request clarification or add additional context in comments.

5 Comments

Are you certain that the initialization of i is correct? If there is an initVal and only a single array element, fun is never called.
@Zirak that's nice. obj = arr.reduce ((o, item) -> o[item.name] = item.value; o), {} in coffeescript.
@Thilo-AlexanderGinkel this is a nice dependable collection of shims: github.com/kriskowal/es5-shim
@Thilo-AlexanderGinkel Nice catch. Fixed and added alternatives.
Somewhat old, but you can simplify the js version to arr.reduce(function ( total, current ) { return total[ current.name ] = current.value, total }, {})
19
values = {}
values[name] = value for {name, value} in arr

or in javascript:

var values = {}
arr.forEach(function(o){
    values[o.name] = o.value
})

Which is almost exactly what the CoffeeScript one compiles to.

Comments

13

Or using plain ES6:

const old = [
  {name: 'a', value: 'b', other: 'c'}, 
  {name: 'd', value: 'e', other: 'f'}
]

const transformed = Object.assign(
  {}, 
  ...old.map(({name, value}) => ({ [name]: value }))
);

console.log(transformed);

3 Comments

this is a hot one liner - but can you explain this part? old.map(({name, value}) => ({ [name]: value })) what is the => ( ) doing? It is returning a key value pair with a variable name, but I don't get the () or the [] part.
It's returning a key value pair with indeed a variable key, being always the name value and the value being the value value of the old objects. So it returns {a: 'b'} which will be assigned to the empty object. Clear?
I spent so much time looking for what I now understand is called computed property names ({[key]: value}), this answer was the clue I needed to finally know what to search for. Thank you!
11

To fix the syntax error, you'll have to expand { @name: @value } to:

o = {}; o[@name] = @value; o

You can then merge the objects with $.extend() and a splat (with the empty object to avoid accidentally extending jQuery):

$.extend {}, $(row).children('input').map(() -> o = {}; o[@name] = @value; o)...

Though, a simpler option would be just to use a 2-liner:

result = {}
$(row).children('input').each(() -> result[@name] = @value)

1 Comment

and if you REALLY must, you can throw the initialization into the callback (result?={})[@name]...
6

Using Array.prototype.reduce():

var arrayOfObjects = [
              {name: 'a', value: 'b', other: 'c'}, 
              {name: 'd', value: 'e', other: 'f'}
            ];

arrayOfObjects.reduce(function(previousValue, currentValue, currentIndex) {
  previousValue[currentValue.name] = currentValue.value;
  return previousValue;
}, {})

Comments

2

ES6 one-liner:

const data = [{name: 'a', value: 97}, {name: 'b', value: 98}]

data.reduce((obj, e) => ({...obj, [e.name]: e.value}), {})

Comments

1

Have a look at http://coffeescriptcookbook.com/chapters/arrays/creating-a-dictionary-object-from-an-array

  myArray = [{name: 'a', value: 'b', other: 'c'}, {name: 'd', value: 'e', other: 'f'}]
  dict = {}
  dict[obj['name']] = obj['value'] for obj in myArray when obj['name']?
  console.log(JSON.stringify(dict, 0, 2));

This produces exactly what you want.

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.