Object key-value map reversal
I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.
For example,
var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};
becomes
var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}
and also a function to bring back reversed map to a non-reversed map.
Here's a jsbin to show that my solution is working.
In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.
Just for sake of clarity, _.forOwn
iterates over object's own properties, and _.forEach
iterates over each element of an array.
var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
var reversedMap = {};
_.forOwn(map, function(value, key) {
key = callback(key);
if (_.isUndefined(reversedMap[value]))
reversedMap[value] = [key];
else
reversedMap[value].push(key);
});
return reversedMap;
};
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || identity;
var map = {};
_.forOwn(reverseMap, function(revMembers, revKey) {
_.forEach(revMembers, function(revMember) {
map[revMember] = callback(revKey);
});
});
return map;
};
I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.
javascript hash-map
add a comment |
I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.
For example,
var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};
becomes
var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}
and also a function to bring back reversed map to a non-reversed map.
Here's a jsbin to show that my solution is working.
In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.
Just for sake of clarity, _.forOwn
iterates over object's own properties, and _.forEach
iterates over each element of an array.
var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
var reversedMap = {};
_.forOwn(map, function(value, key) {
key = callback(key);
if (_.isUndefined(reversedMap[value]))
reversedMap[value] = [key];
else
reversedMap[value].push(key);
});
return reversedMap;
};
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || identity;
var map = {};
_.forOwn(reverseMap, function(revMembers, revKey) {
_.forEach(revMembers, function(revMember) {
map[revMember] = callback(revKey);
});
});
return map;
};
I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.
javascript hash-map
add a comment |
I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.
For example,
var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};
becomes
var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}
and also a function to bring back reversed map to a non-reversed map.
Here's a jsbin to show that my solution is working.
In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.
Just for sake of clarity, _.forOwn
iterates over object's own properties, and _.forEach
iterates over each element of an array.
var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
var reversedMap = {};
_.forOwn(map, function(value, key) {
key = callback(key);
if (_.isUndefined(reversedMap[value]))
reversedMap[value] = [key];
else
reversedMap[value].push(key);
});
return reversedMap;
};
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || identity;
var map = {};
_.forOwn(reverseMap, function(revMembers, revKey) {
_.forEach(revMembers, function(revMember) {
map[revMember] = callback(revKey);
});
});
return map;
};
I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.
javascript hash-map
I have implemented functions that will let me reverse a one-to-many value 'Map', that is a plain JavaScript object.
For example,
var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};
becomes
var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}
and also a function to bring back reversed map to a non-reversed map.
Here's a jsbin to show that my solution is working.
In the following code I have used lo-dash utility toolbelt, but you need not be acquainted with it in order to understand the code. Assume that I trust in performance of lo-dash and what I'm trying to optimize is my own code.
Just for sake of clarity, _.forOwn
iterates over object's own properties, and _.forEach
iterates over each element of an array.
var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
var reversedMap = {};
_.forOwn(map, function(value, key) {
key = callback(key);
if (_.isUndefined(reversedMap[value]))
reversedMap[value] = [key];
else
reversedMap[value].push(key);
});
return reversedMap;
};
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || identity;
var map = {};
_.forOwn(reverseMap, function(revMembers, revKey) {
_.forEach(revMembers, function(revMember) {
map[revMember] = callback(revKey);
});
});
return map;
};
I'm pretty confident in my solution as it's quite basic, but as it is a frequent part of my code, I'd like to know if you can see any ways to improve performance, or alternatively if there's a better way to solve this problem.
javascript hash-map
javascript hash-map
edited Sep 29 '14 at 2:24
Jamal♦
30.3k11116226
30.3k11116226
asked Aug 31 '14 at 16:29
Peeyush KushwahaPeeyush Kushwaha
158119
158119
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.
One thing I noticed is that you've skipped braces on the if...else
in reverseFromMap
. My advice is to always use braces, even for one-line expressions.
However, you could also do
reversedMap[value] || (reversedMap[value] = );
reversedMap[value].push(key);
You can collapse that into 1 line, although it's a little too terse if you ask me
(reversedMap[value] || (reversedMap[value] = )).push(key);
// or
(reversedMap[value] = reversedMap[value] || ).push(key);
Or just reduce it to a ternary
reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];
The _.isUndefined
check isn't really required, since reversedMap[key]
is either a non-empty array, or it's false'y.
Other small stuff:
I'd prefer the name
iterator
instead ofcallback
, but lo-dash usescallback
in similar situations, so sticking with that convention is fine.
You don't necessarily need the
identity
function, as you could also do
x = callback ? callback(x) : x;
and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.
If you do need an identity function, lo-dash has one built-in (
_.identity
). Saves you having to type it out.
Lastly, if you want to be stringent, you might want to check whether
callback
is a function, instead of just doing "default if false'y". E.g.
callback = _.isFunction(callback) ? callback : _.identity;
// or just plain JS
callback = typeof callback === 'function' ? callback : _.identity;
Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty)
callback
argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.
As for alternatives to the overall approach, I'd use _.transform
(which is basically the same as reduce
/foldl
but aimed at objects)
var reverseMapFromMap = function(map, callback) {
callback = callback || _.identity;
return _.transform(map, function (memo, value, key) {
key = callback(key);
memo[value] || (memo[value] = );
memo[value].push(key);
}, {});
}
And again for mapFromReverseMap
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || _.identity;
return _.transform(reverseMap, function (memo, keys, value) {
_.forEach(keys, function (key) {
memo[callback(key)] = value;
});
}, {});
};
2
Nice, but I thinkcallback = callback || _.identity
is better thancallback || (callback = _.identity)
. It's natural that way, no need to mess with it.
– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
add a comment |
I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys
and reduce
:
var reverseMapFromMap = function(map, f) {
return Object.keys(map).reduce(function(acc, k) {
acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
return acc
},{})
}
var mapFromReverseMap = function(rMap, f) {
return Object.keys(rMap).reduce(function(acc, k) {
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
return acc
},{})
}
I renamed identity
to id
, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.
The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.
Other than the missing braces in the if
statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f
clearly means "function" for example, and x
is any value, where k
is a "key". After getting used to these conventions, a line like:
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
seems faster to visually parse than:
rMap[key].forEach(function(value) {
acc[value] = (callback || identity)(key)
})
Eventually your mind removes the function
keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:
rMap[k].forEach(x => acc[x] = (f || id)(k))
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
add a comment |
a million ways o solve the same problem of a reverse map...
mine is like this...
simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.
messages={messages.slice(0).reverse().map(
message => ({ <p>{message}</p> )}
)}
New contributor
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61632%2fobject-key-value-map-reversal%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.
One thing I noticed is that you've skipped braces on the if...else
in reverseFromMap
. My advice is to always use braces, even for one-line expressions.
However, you could also do
reversedMap[value] || (reversedMap[value] = );
reversedMap[value].push(key);
You can collapse that into 1 line, although it's a little too terse if you ask me
(reversedMap[value] || (reversedMap[value] = )).push(key);
// or
(reversedMap[value] = reversedMap[value] || ).push(key);
Or just reduce it to a ternary
reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];
The _.isUndefined
check isn't really required, since reversedMap[key]
is either a non-empty array, or it's false'y.
Other small stuff:
I'd prefer the name
iterator
instead ofcallback
, but lo-dash usescallback
in similar situations, so sticking with that convention is fine.
You don't necessarily need the
identity
function, as you could also do
x = callback ? callback(x) : x;
and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.
If you do need an identity function, lo-dash has one built-in (
_.identity
). Saves you having to type it out.
Lastly, if you want to be stringent, you might want to check whether
callback
is a function, instead of just doing "default if false'y". E.g.
callback = _.isFunction(callback) ? callback : _.identity;
// or just plain JS
callback = typeof callback === 'function' ? callback : _.identity;
Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty)
callback
argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.
As for alternatives to the overall approach, I'd use _.transform
(which is basically the same as reduce
/foldl
but aimed at objects)
var reverseMapFromMap = function(map, callback) {
callback = callback || _.identity;
return _.transform(map, function (memo, value, key) {
key = callback(key);
memo[value] || (memo[value] = );
memo[value].push(key);
}, {});
}
And again for mapFromReverseMap
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || _.identity;
return _.transform(reverseMap, function (memo, keys, value) {
_.forEach(keys, function (key) {
memo[callback(key)] = value;
});
}, {});
};
2
Nice, but I thinkcallback = callback || _.identity
is better thancallback || (callback = _.identity)
. It's natural that way, no need to mess with it.
– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
add a comment |
Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.
One thing I noticed is that you've skipped braces on the if...else
in reverseFromMap
. My advice is to always use braces, even for one-line expressions.
However, you could also do
reversedMap[value] || (reversedMap[value] = );
reversedMap[value].push(key);
You can collapse that into 1 line, although it's a little too terse if you ask me
(reversedMap[value] || (reversedMap[value] = )).push(key);
// or
(reversedMap[value] = reversedMap[value] || ).push(key);
Or just reduce it to a ternary
reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];
The _.isUndefined
check isn't really required, since reversedMap[key]
is either a non-empty array, or it's false'y.
Other small stuff:
I'd prefer the name
iterator
instead ofcallback
, but lo-dash usescallback
in similar situations, so sticking with that convention is fine.
You don't necessarily need the
identity
function, as you could also do
x = callback ? callback(x) : x;
and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.
If you do need an identity function, lo-dash has one built-in (
_.identity
). Saves you having to type it out.
Lastly, if you want to be stringent, you might want to check whether
callback
is a function, instead of just doing "default if false'y". E.g.
callback = _.isFunction(callback) ? callback : _.identity;
// or just plain JS
callback = typeof callback === 'function' ? callback : _.identity;
Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty)
callback
argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.
As for alternatives to the overall approach, I'd use _.transform
(which is basically the same as reduce
/foldl
but aimed at objects)
var reverseMapFromMap = function(map, callback) {
callback = callback || _.identity;
return _.transform(map, function (memo, value, key) {
key = callback(key);
memo[value] || (memo[value] = );
memo[value].push(key);
}, {});
}
And again for mapFromReverseMap
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || _.identity;
return _.transform(reverseMap, function (memo, keys, value) {
_.forEach(keys, function (key) {
memo[callback(key)] = value;
});
}, {});
};
2
Nice, but I thinkcallback = callback || _.identity
is better thancallback || (callback = _.identity)
. It's natural that way, no need to mess with it.
– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
add a comment |
Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.
One thing I noticed is that you've skipped braces on the if...else
in reverseFromMap
. My advice is to always use braces, even for one-line expressions.
However, you could also do
reversedMap[value] || (reversedMap[value] = );
reversedMap[value].push(key);
You can collapse that into 1 line, although it's a little too terse if you ask me
(reversedMap[value] || (reversedMap[value] = )).push(key);
// or
(reversedMap[value] = reversedMap[value] || ).push(key);
Or just reduce it to a ternary
reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];
The _.isUndefined
check isn't really required, since reversedMap[key]
is either a non-empty array, or it's false'y.
Other small stuff:
I'd prefer the name
iterator
instead ofcallback
, but lo-dash usescallback
in similar situations, so sticking with that convention is fine.
You don't necessarily need the
identity
function, as you could also do
x = callback ? callback(x) : x;
and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.
If you do need an identity function, lo-dash has one built-in (
_.identity
). Saves you having to type it out.
Lastly, if you want to be stringent, you might want to check whether
callback
is a function, instead of just doing "default if false'y". E.g.
callback = _.isFunction(callback) ? callback : _.identity;
// or just plain JS
callback = typeof callback === 'function' ? callback : _.identity;
Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty)
callback
argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.
As for alternatives to the overall approach, I'd use _.transform
(which is basically the same as reduce
/foldl
but aimed at objects)
var reverseMapFromMap = function(map, callback) {
callback = callback || _.identity;
return _.transform(map, function (memo, value, key) {
key = callback(key);
memo[value] || (memo[value] = );
memo[value].push(key);
}, {});
}
And again for mapFromReverseMap
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || _.identity;
return _.transform(reverseMap, function (memo, keys, value) {
_.forEach(keys, function (key) {
memo[callback(key)] = value;
});
}, {});
};
Your code is pretty good as-is. There are other ways to do the same thing, but yours is fine.
One thing I noticed is that you've skipped braces on the if...else
in reverseFromMap
. My advice is to always use braces, even for one-line expressions.
However, you could also do
reversedMap[value] || (reversedMap[value] = );
reversedMap[value].push(key);
You can collapse that into 1 line, although it's a little too terse if you ask me
(reversedMap[value] || (reversedMap[value] = )).push(key);
// or
(reversedMap[value] = reversedMap[value] || ).push(key);
Or just reduce it to a ternary
reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];
The _.isUndefined
check isn't really required, since reversedMap[key]
is either a non-empty array, or it's false'y.
Other small stuff:
I'd prefer the name
iterator
instead ofcallback
, but lo-dash usescallback
in similar situations, so sticking with that convention is fine.
You don't necessarily need the
identity
function, as you could also do
x = callback ? callback(x) : x;
and just not bother with the identity function. It may or may not be faster - I haven't benchmarked it. In any event, both approaches work just fine, so there's no need to change anything.
If you do need an identity function, lo-dash has one built-in (
_.identity
). Saves you having to type it out.
Lastly, if you want to be stringent, you might want to check whether
callback
is a function, instead of just doing "default if false'y". E.g.
callback = _.isFunction(callback) ? callback : _.identity;
// or just plain JS
callback = typeof callback === 'function' ? callback : _.identity;
Personally, I'd skip this, though. If people pass in something that's not a function, it's their bug to deal with. Besides, I'd rather have the code fail loudly than having it silently ignore my (faulty)
callback
argument and default to something else without telling me. Still, type-checking is an option, so I thought I'd mention it.
As for alternatives to the overall approach, I'd use _.transform
(which is basically the same as reduce
/foldl
but aimed at objects)
var reverseMapFromMap = function(map, callback) {
callback = callback || _.identity;
return _.transform(map, function (memo, value, key) {
key = callback(key);
memo[value] || (memo[value] = );
memo[value].push(key);
}, {});
}
And again for mapFromReverseMap
var mapFromReverseMap = function(reverseMap, callback) {
callback = callback || _.identity;
return _.transform(reverseMap, function (memo, keys, value) {
_.forEach(keys, function (key) {
memo[callback(key)] = value;
});
}, {});
};
edited Aug 31 '14 at 19:57
answered Aug 31 '14 at 17:18
FlambinoFlambino
31.6k23386
31.6k23386
2
Nice, but I thinkcallback = callback || _.identity
is better thancallback || (callback = _.identity)
. It's natural that way, no need to mess with it.
– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
add a comment |
2
Nice, but I thinkcallback = callback || _.identity
is better thancallback || (callback = _.identity)
. It's natural that way, no need to mess with it.
– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
2
2
Nice, but I think
callback = callback || _.identity
is better than callback || (callback = _.identity)
. It's natural that way, no need to mess with it.– janos
Aug 31 '14 at 17:34
Nice, but I think
callback = callback || _.identity
is better than callback || (callback = _.identity)
. It's natural that way, no need to mess with it.– janos
Aug 31 '14 at 17:34
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
@janos Heh, hadn't even noticed that I changed that. It wasn't on purpose. And you're right, the version without the parens is more natural, though I personally don't mind either one. Still, I'll update my answer
– Flambino
Aug 31 '14 at 17:52
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
Thanks @Flambino! I employed some of your suggestions and have updated the question with the final code.
– Peeyush Kushwaha
Sep 1 '14 at 10:15
add a comment |
I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys
and reduce
:
var reverseMapFromMap = function(map, f) {
return Object.keys(map).reduce(function(acc, k) {
acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
return acc
},{})
}
var mapFromReverseMap = function(rMap, f) {
return Object.keys(rMap).reduce(function(acc, k) {
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
return acc
},{})
}
I renamed identity
to id
, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.
The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.
Other than the missing braces in the if
statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f
clearly means "function" for example, and x
is any value, where k
is a "key". After getting used to these conventions, a line like:
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
seems faster to visually parse than:
rMap[key].forEach(function(value) {
acc[value] = (callback || identity)(key)
})
Eventually your mind removes the function
keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:
rMap[k].forEach(x => acc[x] = (f || id)(k))
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
add a comment |
I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys
and reduce
:
var reverseMapFromMap = function(map, f) {
return Object.keys(map).reduce(function(acc, k) {
acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
return acc
},{})
}
var mapFromReverseMap = function(rMap, f) {
return Object.keys(rMap).reduce(function(acc, k) {
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
return acc
},{})
}
I renamed identity
to id
, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.
The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.
Other than the missing braces in the if
statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f
clearly means "function" for example, and x
is any value, where k
is a "key". After getting used to these conventions, a line like:
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
seems faster to visually parse than:
rMap[key].forEach(function(value) {
acc[value] = (callback || identity)(key)
})
Eventually your mind removes the function
keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:
rMap[k].forEach(x => acc[x] = (f || id)(k))
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
add a comment |
I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys
and reduce
:
var reverseMapFromMap = function(map, f) {
return Object.keys(map).reduce(function(acc, k) {
acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
return acc
},{})
}
var mapFromReverseMap = function(rMap, f) {
return Object.keys(rMap).reduce(function(acc, k) {
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
return acc
},{})
}
I renamed identity
to id
, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.
The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.
Other than the missing braces in the if
statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f
clearly means "function" for example, and x
is any value, where k
is a "key". After getting used to these conventions, a line like:
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
seems faster to visually parse than:
rMap[key].forEach(function(value) {
acc[value] = (callback || identity)(key)
})
Eventually your mind removes the function
keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:
rMap[k].forEach(x => acc[x] = (f || id)(k))
I would recommend using built-in methods when you can. In this case I doesn't look like you need LoDash at all. You can use Object.keys
and reduce
:
var reverseMapFromMap = function(map, f) {
return Object.keys(map).reduce(function(acc, k) {
acc[map[k]] = (acc[map[k]] || ).concat((f || id)(k))
return acc
},{})
}
var mapFromReverseMap = function(rMap, f) {
return Object.keys(rMap).reduce(function(acc, k) {
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
return acc
},{})
}
I renamed identity
to id
, as it is a well known function in functional programming. I don't think it would create confusion, and lets you pass it in one liners easily, like shown above.
The above works with your example http://jsbin.com/cadig/1/edit. But I would recommend you reverse the arguments, so the callback comes first, and the receiver last, so it works better with composition.
Other than the missing braces in the if
statement, I'd say your code looks pretty good. It is quite common though to use one or two letter variables in functional programming, as these transformations shall apply to many things in theory (polymorphism), and we only have a handful of objects. f
clearly means "function" for example, and x
is any value, where k
is a "key". After getting used to these conventions, a line like:
rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
seems faster to visually parse than:
rMap[key].forEach(function(value) {
acc[value] = (callback || identity)(key)
})
Eventually your mind removes the function
keyword, and you just see the transformation as it is. In ES6 this becomes even shorter, and calls for a concise one-liner:
rMap[k].forEach(x => acc[x] = (f || id)(k))
edited Aug 31 '14 at 23:08
answered Aug 31 '14 at 22:38
elclanrselclanrs
2,3221016
2,3221016
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
add a comment |
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
+1 for "native" functions. I consciously chose to stick to lo-dash in my answer, just for maximum compatibility. Besides, lo-dash will, I believe, just forward as much as possible to the native implementations, if available. Still, the more built-in functions you can use, the better.
– Flambino
Sep 1 '14 at 8:27
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
On the contrary, lodash is faster than native for most cases. This is primarily because it does not handle obscure use cases.
– Peeyush Kushwaha
Sep 1 '14 at 9:54
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
Native solution didn't turn out so well, performance-wise. I have updated the question with some performance information.
– Peeyush Kushwaha
Sep 1 '14 at 10:16
add a comment |
a million ways o solve the same problem of a reverse map...
mine is like this...
simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.
messages={messages.slice(0).reverse().map(
message => ({ <p>{message}</p> )}
)}
New contributor
add a comment |
a million ways o solve the same problem of a reverse map...
mine is like this...
simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.
messages={messages.slice(0).reverse().map(
message => ({ <p>{message}</p> )}
)}
New contributor
add a comment |
a million ways o solve the same problem of a reverse map...
mine is like this...
simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.
messages={messages.slice(0).reverse().map(
message => ({ <p>{message}</p> )}
)}
New contributor
a million ways o solve the same problem of a reverse map...
mine is like this...
simple and elegant, dontcha think? just add 'slice(0).reverse()' in between the item and map. use it for reverse inserting things to the top instead of the bottom of a list, or whatever you're using it for.
messages={messages.slice(0).reverse().map(
message => ({ <p>{message}</p> )}
)}
New contributor
New contributor
answered 4 mins ago
KG ConsultsKG Consults
1
1
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f61632%2fobject-key-value-map-reversal%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown