Group array of hashes on key and append nested array on another hash key
I need to reduce an array of hashes into a single hash if that hash key matches.
my_array
is an array of hashes, each of which has two keys: one an active record object, the other an array of different active record objects, similar to:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff", "more stuff", "more stuff"]
},
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff 3", "uniq stuff 2"]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I want to combine hashes by first_hash_key[:id]
, add each item in hash_key_two
to an array, not overwrite them to get:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: [
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I can reduce the top level array and hash, and use merge
, but I'm losing the individual arrays inside the hash.
I've also tried grouping by the id
of the first_hash_key
, and then injecting like this:
my_array.group_by{|h| h[:first_hash_key]}.map{|k,v| v.inject(:merge)}
Again, I'm losing the arrays in the second_hash_key
. I only get the last array listed. Without map
, I get an array of each hash in the group, but the top level isn't combined.
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>["stuff 3", "uniq stuff 2"]
},
{
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]
}
]
Update
As noted by sawa and cary, there is no sense in guessing for the second_hash_key if the data is an array or array or arrays, better to always have an array of arrays. The desired output is:
[{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]
},
{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[["interesting stuff", "uniq stuff"]]
}]
ruby
add a comment |
I need to reduce an array of hashes into a single hash if that hash key matches.
my_array
is an array of hashes, each of which has two keys: one an active record object, the other an array of different active record objects, similar to:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff", "more stuff", "more stuff"]
},
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff 3", "uniq stuff 2"]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I want to combine hashes by first_hash_key[:id]
, add each item in hash_key_two
to an array, not overwrite them to get:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: [
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I can reduce the top level array and hash, and use merge
, but I'm losing the individual arrays inside the hash.
I've also tried grouping by the id
of the first_hash_key
, and then injecting like this:
my_array.group_by{|h| h[:first_hash_key]}.map{|k,v| v.inject(:merge)}
Again, I'm losing the arrays in the second_hash_key
. I only get the last array listed. Without map
, I get an array of each hash in the group, but the top level isn't combined.
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>["stuff 3", "uniq stuff 2"]
},
{
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]
}
]
Update
As noted by sawa and cary, there is no sense in guessing for the second_hash_key if the data is an array or array or arrays, better to always have an array of arrays. The desired output is:
[{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]
},
{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[["interesting stuff", "uniq stuff"]]
}]
ruby
Where ishash_key_two
?
– sawa
Nov 27 '18 at 9:11
Supposemy_array[1][:first_hash_key]
equaled{id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of:id
they have the same value of:title
.
– Cary Swoveland
Nov 27 '18 at 9:27
1
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of:second_hash_key
be an array of arrays, even when it contains just one array.
– Cary Swoveland
Nov 27 '18 at 9:37
add a comment |
I need to reduce an array of hashes into a single hash if that hash key matches.
my_array
is an array of hashes, each of which has two keys: one an active record object, the other an array of different active record objects, similar to:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff", "more stuff", "more stuff"]
},
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff 3", "uniq stuff 2"]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I want to combine hashes by first_hash_key[:id]
, add each item in hash_key_two
to an array, not overwrite them to get:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: [
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I can reduce the top level array and hash, and use merge
, but I'm losing the individual arrays inside the hash.
I've also tried grouping by the id
of the first_hash_key
, and then injecting like this:
my_array.group_by{|h| h[:first_hash_key]}.map{|k,v| v.inject(:merge)}
Again, I'm losing the arrays in the second_hash_key
. I only get the last array listed. Without map
, I get an array of each hash in the group, but the top level isn't combined.
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>["stuff 3", "uniq stuff 2"]
},
{
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]
}
]
Update
As noted by sawa and cary, there is no sense in guessing for the second_hash_key if the data is an array or array or arrays, better to always have an array of arrays. The desired output is:
[{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]
},
{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[["interesting stuff", "uniq stuff"]]
}]
ruby
I need to reduce an array of hashes into a single hash if that hash key matches.
my_array
is an array of hashes, each of which has two keys: one an active record object, the other an array of different active record objects, similar to:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff", "more stuff", "more stuff"]
},
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: ["stuff 3", "uniq stuff 2"]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I want to combine hashes by first_hash_key[:id]
, add each item in hash_key_two
to an array, not overwrite them to get:
my_array = [
{
first_hash_key: {id: 1, title: "my title"},
second_hash_key: [
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
},
{
first_hash_key: {id: 2, title: "my other title"},
second_hash_key: ["interesting stuff", "uniq stuff"]
}
]
I can reduce the top level array and hash, and use merge
, but I'm losing the individual arrays inside the hash.
I've also tried grouping by the id
of the first_hash_key
, and then injecting like this:
my_array.group_by{|h| h[:first_hash_key]}.map{|k,v| v.inject(:merge)}
Again, I'm losing the arrays in the second_hash_key
. I only get the last array listed. Without map
, I get an array of each hash in the group, but the top level isn't combined.
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>["stuff 3", "uniq stuff 2"]
},
{
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]
}
]
Update
As noted by sawa and cary, there is no sense in guessing for the second_hash_key if the data is an array or array or arrays, better to always have an array of arrays. The desired output is:
[{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]
},
{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[["interesting stuff", "uniq stuff"]]
}]
ruby
ruby
edited Nov 28 '18 at 2:40
trh
asked Nov 27 '18 at 1:54
trhtrh
6,10411733
6,10411733
Where ishash_key_two
?
– sawa
Nov 27 '18 at 9:11
Supposemy_array[1][:first_hash_key]
equaled{id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of:id
they have the same value of:title
.
– Cary Swoveland
Nov 27 '18 at 9:27
1
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of:second_hash_key
be an array of arrays, even when it contains just one array.
– Cary Swoveland
Nov 27 '18 at 9:37
add a comment |
Where ishash_key_two
?
– sawa
Nov 27 '18 at 9:11
Supposemy_array[1][:first_hash_key]
equaled{id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of:id
they have the same value of:title
.
– Cary Swoveland
Nov 27 '18 at 9:27
1
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of:second_hash_key
be an array of arrays, even when it contains just one array.
– Cary Swoveland
Nov 27 '18 at 9:37
Where is
hash_key_two
?– sawa
Nov 27 '18 at 9:11
Where is
hash_key_two
?– sawa
Nov 27 '18 at 9:11
Suppose
my_array[1][:first_hash_key]
equaled {id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of :id
they have the same value of :title
.– Cary Swoveland
Nov 27 '18 at 9:27
Suppose
my_array[1][:first_hash_key]
equaled {id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of :id
they have the same value of :title
.– Cary Swoveland
Nov 27 '18 at 9:27
1
1
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of
:second_hash_key
be an array of arrays, even when it contains just one array.– Cary Swoveland
Nov 27 '18 at 9:37
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of
:second_hash_key
be an array of arrays, even when it contains just one array.– Cary Swoveland
Nov 27 '18 at 9:37
add a comment |
2 Answers
2
active
oldest
votes
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce() do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of:second_hash_key
in the hash where[:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.
– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
add a comment |
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>["interesting stuff", "uniq stuff"]}]
The receiver of Hash#values is the hash:
{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]},
2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]}}
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged, here the keys being 1
and 2
. That block has three block variables: _
equals the common key1; o
("old") is value of the key _
in the hash being constructed and n
("new") is the value of the key _
in the hash being merged into the hash being constructed.
The expression [*o[:second_hash_key]]
converts o[:second_hash_key]
to an array of itself if o[:second_hash_key]
is not an array and leaves o[:second_hash_key]
unchanged if it is already an array. For example, [*1] #=> [1]
, whereas [*[1,2]] #=> [1,2]
.
The return value is problematic in that the value of :second_hash_key
is in one case an array of arrays and in another it is simply an array. In subsequent calculations it therefore will be necessary to determine if each value is an array of arrays or just an array, and then take action accordingly. That could be done, of course, but it's messy and ugly; better to make all the values arrays of arrays. We could do that as follows.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>
g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]
1 The use of the underscore--a valid local variable--for the common key is to inform the reader that it is not used in the block calculation.
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
add a comment |
Your Answer
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: "1"
};
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: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
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%2fstackoverflow.com%2fquestions%2f53491664%2fgroup-array-of-hashes-on-key-and-append-nested-array-on-another-hash-key%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce() do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of:second_hash_key
in the hash where[:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.
– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
add a comment |
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce() do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of:second_hash_key
in the hash where[:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.
– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
add a comment |
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce() do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce() do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
edited Nov 27 '18 at 3:39
answered Nov 27 '18 at 3:33
max pleanermax pleaner
13.9k42167
13.9k42167
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of:second_hash_key
in the hash where[:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.
– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
add a comment |
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of:second_hash_key
in the hash where[:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.
– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
1
1
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of
:second_hash_key
in the hash where [:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.– sawa
Nov 27 '18 at 9:18
The result of your code is more consistent and makes sense, but is different from what OP wanted. For the value of
:second_hash_key
in the hash where [:first_hash_key][:id] == 2
, the OP did not want a nested array. What OP wanted is inconsistent and makes less sense, but is not what your code returns.– sawa
Nov 27 '18 at 9:18
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
Actually - I do want a nested array, and max's code returns a nested array - I incorrectly posted my desired return. I need to be able to iterate over the arrays from second_hash_key. I can update the question
– trh
Nov 28 '18 at 2:37
add a comment |
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>["interesting stuff", "uniq stuff"]}]
The receiver of Hash#values is the hash:
{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]},
2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]}}
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged, here the keys being 1
and 2
. That block has three block variables: _
equals the common key1; o
("old") is value of the key _
in the hash being constructed and n
("new") is the value of the key _
in the hash being merged into the hash being constructed.
The expression [*o[:second_hash_key]]
converts o[:second_hash_key]
to an array of itself if o[:second_hash_key]
is not an array and leaves o[:second_hash_key]
unchanged if it is already an array. For example, [*1] #=> [1]
, whereas [*[1,2]] #=> [1,2]
.
The return value is problematic in that the value of :second_hash_key
is in one case an array of arrays and in another it is simply an array. In subsequent calculations it therefore will be necessary to determine if each value is an array of arrays or just an array, and then take action accordingly. That could be done, of course, but it's messy and ugly; better to make all the values arrays of arrays. We could do that as follows.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>
g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]
1 The use of the underscore--a valid local variable--for the common key is to inform the reader that it is not used in the block calculation.
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
add a comment |
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>["interesting stuff", "uniq stuff"]}]
The receiver of Hash#values is the hash:
{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]},
2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]}}
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged, here the keys being 1
and 2
. That block has three block variables: _
equals the common key1; o
("old") is value of the key _
in the hash being constructed and n
("new") is the value of the key _
in the hash being merged into the hash being constructed.
The expression [*o[:second_hash_key]]
converts o[:second_hash_key]
to an array of itself if o[:second_hash_key]
is not an array and leaves o[:second_hash_key]
unchanged if it is already an array. For example, [*1] #=> [1]
, whereas [*[1,2]] #=> [1,2]
.
The return value is problematic in that the value of :second_hash_key
is in one case an array of arrays and in another it is simply an array. In subsequent calculations it therefore will be necessary to determine if each value is an array of arrays or just an array, and then take action accordingly. That could be done, of course, but it's messy and ugly; better to make all the values arrays of arrays. We could do that as follows.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>
g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]
1 The use of the underscore--a valid local variable--for the common key is to inform the reader that it is not used in the block calculation.
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
add a comment |
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>["interesting stuff", "uniq stuff"]}]
The receiver of Hash#values is the hash:
{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]},
2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]}}
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged, here the keys being 1
and 2
. That block has three block variables: _
equals the common key1; o
("old") is value of the key _
in the hash being constructed and n
("new") is the value of the key _
in the hash being merged into the hash being constructed.
The expression [*o[:second_hash_key]]
converts o[:second_hash_key]
to an array of itself if o[:second_hash_key]
is not an array and leaves o[:second_hash_key]
unchanged if it is already an array. For example, [*1] #=> [1]
, whereas [*[1,2]] #=> [1,2]
.
The return value is problematic in that the value of :second_hash_key
is in one case an array of arrays and in another it is simply an array. In subsequent calculations it therefore will be necessary to determine if each value is an array of arrays or just an array, and then take action accordingly. That could be done, of course, but it's messy and ugly; better to make all the values arrays of arrays. We could do that as follows.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>
g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]
1 The use of the underscore--a valid local variable--for the common key is to inform the reader that it is not used in the block calculation.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>["interesting stuff", "uniq stuff"]}]
The receiver of Hash#values is the hash:
{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]]},
2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>["interesting stuff", "uniq stuff"]}}
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged, here the keys being 1
and 2
. That block has three block variables: _
equals the common key1; o
("old") is value of the key _
in the hash being constructed and n
("new") is the value of the key _
in the hash being merged into the hash being constructed.
The expression [*o[:second_hash_key]]
converts o[:second_hash_key]
to an array of itself if o[:second_hash_key]
is not an array and leaves o[:second_hash_key]
unchanged if it is already an array. For example, [*1] #=> [1]
, whereas [*[1,2]] #=> [1,2]
.
The return value is problematic in that the value of :second_hash_key
is in one case an array of arrays and in another it is simply an array. In subsequent calculations it therefore will be necessary to determine if each value is an array of arrays or just an array, and then take action accordingly. That could be done, of course, but it's messy and ugly; better to make all the values arrays of arrays. We could do that as follows.
my_array.each_with_object({}) do |g,h|
h.update(g[:first_hash_key][:id]=>
g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
#=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
# :second_hash_key=>[["stuff", "more stuff", "more stuff"],
# ["stuff 3", "uniq stuff 2"]]},
# {:first_hash_key=>{:id=>2, :title=>"my other title"},
# :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]
1 The use of the underscore--a valid local variable--for the common key is to inform the reader that it is not used in the block calculation.
answered Nov 27 '18 at 19:13
Cary SwovelandCary Swoveland
69.8k54166
69.8k54166
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
add a comment |
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
So noted. I agree, too much work to guess at the data within second_hash_key - updated output required. thanks!!!!
– trh
Nov 28 '18 at 2:40
add a comment |
Thanks for contributing an answer to Stack Overflow!
- 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%2fstackoverflow.com%2fquestions%2f53491664%2fgroup-array-of-hashes-on-key-and-append-nested-array-on-another-hash-key%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
Where is
hash_key_two
?– sawa
Nov 27 '18 at 9:11
Suppose
my_array[1][:first_hash_key]
equaled{id: 1, title: "my dog"}
. How (if at all) would that change the desired result. Perhaps if two hashes have the same value of:id
they have the same value of:title
.– Cary Swoveland
Nov 27 '18 at 9:27
1
As @sawa intimated in a comment below you are making life unnecessarily difficult for yourself by not always having the value of
:second_hash_key
be an array of arrays, even when it contains just one array.– Cary Swoveland
Nov 27 '18 at 9:37