Reputation: 6428
is there a way to return an object from a comprehension in coffeescript? something so that i could express this:
form_values = () ->
ret = {}
ret[f.name] = f.value for f in $('input, textarea, select')
return ret
like this:
form_values = () -> f.name, f.value for f in $('input, textarea, select')
i'd like to construct a single object (not an array of objects). so if the markup looks something like this:
<form name=blah>
<input type=text name=blah1 value=111 />
<textarea name=blah2>222</textarea>
<select name=blah3>
<option value=333a>
<option value=333b>
</select>
</form>
the returned object would be something like this:
{
blah1: '111',
blah2: '222',
blah3: ''
}
Upvotes: 43
Views: 7986
Reputation: 131
Not to beat a dead horse, but I personally thing this is readable and satisfies the 'one line' requirement without needing extra modules:
form_values = {}; form_values[f.name] = f.value for f in $('input, textarea, select')
Don't forget you can still use a semi-colon to combine lines in Coffeescript!
Upvotes: 1
Reputation: 5363
Using underscore's object function, you can do this:
form_values = _.object([f.name, f.value] for f in $('input, textarea, select'))
Upvotes: 7
Reputation: 18811
CoffeeScript's creator suggests using a helper function to convert an array of pairs into an object:
form_values = toObject([f.name, f.value] for f in $('input, textarea, select'))
This is arguably the most readable way of doing it, within the current language syntax. It's also very similar to how Python and other languages do it, except for the missing syntactic sugar.
The helper function can be easily written once, using for example the technique from @matyr's and @Sylvain's answers:
// Create a new object from an array of [key, value] pairs.
toObject = (pairs) ->
new -> @[key] = value for [key, value] in pairs; @
Upvotes: 3
Reputation: 52030
This has already been answered but probably lack of some explanations as this idiom is rather cryptic at first sight:
form_values = (new -> @[f.name] = f.value for f in $ 'input, textarea, select'; @)
// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
// create with |
// a new that |
// empty anonymous |
// object constructor |
// don't forget -/
// to return the
// newly created object
The key idea is to create an empty object (new
) with an anonymous constructor (-> ...
) that will create the various fields.
Upvotes: 4
Reputation: 93
I believe you can do this with no added libraries right in CoffeeScript.
It should be something to the effect of:
$('input, textarea, select').each (item) => @form_values || @form_values = {}; @form_values[$(item).name] = $(item).value
You could simplify the syntax of that by pre-creating the form_values:
form_values = {}
$('input, textarea, select').each (item) -> form_values[$(item).name] = $(item).value
Here is a lengthier response with canned examples:
Take a very simple example where you wanted to map the obj to name value:
items = [ { a: 1 }, { b: 2 }, { c: 3 } ]
items.map((item) -> {name: Object.keys(item)[0], value: item[Object.keys(item)[0]]})
[ { name: 'a', value: 1 }, { name: 'b', value: 2 }, { name: 'c', value: 3 } ]
Note that the above is not really an Object comprehension, just demonstrating an example.
Now let's say there is a bit more structure and you just want to map a known unique key:
items = [{key: "abc", someVar: 1}, {key: "def", someVar: 2}]
In Python you'd do something simple like this: {x['key']:x for x in items}
In CoffeeScript you can get all of this down to one single line though with a caveat:
items.forEach (item) => @x || @x = {}; @x[item['key']] = item
{ abc: { key: 'abc', someVar: 1 }, def: { key: 'def', someVar: 2 } }
In the above code x was not previously defined in the scope, so using =>
and @
allowed us to bind x
with the @x || @x = {}
if not previously found, then set the key.
If you don't want to use => and @ you have to define x beforehand:
x = {}
items.forEach (item) => x || x = {}; x[item['key']] = item
{ abc: { key: 'abc', someVar: 1 }, def: { key: 'def', someVar: 2 } }
Upvotes: 2
Reputation: 67900
Check the functional library underscore and the extension _.mash
from this mixin:
form_values = ->
_($('input, textarea, select')).mash f -> [f.name, f.value]
Upvotes: 7
Reputation: 5774
form_values = new ->
@[f.name] = f.value for f in $ 'input, textarea, select'
this
or
form_values = new class then constructor: ->
@[f.name] = f.value for f in $ 'input, textarea, select'
Upvotes: 26
Reputation: 77416
Nope. Comprehensions only return arrays in CoffeeScript. Search the issue tracker for object comprehensions, and you'll find several proposals, but none were found suitable.
Upvotes: 23