I am attempting to combine two hashes together, and am having trouble figuring out the best method to do it. I feel like there must be an easy way to do it with enumerables...
I'd like to turn this:
[{ id: 5, count: 10 }, { id: 6, count: -3 }, { id: 5, count: -2 }, { id: 3, count: 4}]
into this:
[{ id: 5, count: 8 }, { id: 6, count: -3 }, { id: 3, count: 4}]
So that hashes with the same "id" are summed together. Anyone have any ideas on how to quickly do this?
I tried Hash.merge but that didn't work correctly...
Here is a way :
hash = [{ id: 5, count: 10 }, { id: 6, count: -3 }, { id: 5, count: -2 }, { id: 3, count: 4}]
merged_hash = hash.group_by { |h| h[:id] }.map do |_,v|
v.reduce do |h1,h2|
h1.merge(h2) { |k,o,n| k == :count ? o + n : o }
end
end
merged_hash
# => [{:id=>5, :count=>8}, {:id=>6, :count=>-3}, {:id=>3, :count=>4}]
Look at these methods Hash#merge, Enumerable#reduce and Enumerable#group_by.
Related
Sorry for the bad English Title but the code below would make more sense.
I used .each to get some keys and values that I wanted from the data but when I do it I get this result.
[
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
I want to map the objects to some js library for which I want it to be like:
[
{
id: 1,
name: "Immad",
age: 18
},
{
id: 2,
name: "Vicky",
age: 21
},
{
id: 3,
name: "Adam",
age: 24
}
]
Can anybody please atleast give me hint what should I use in order to do it using ruby. Thanks in advance.
There already is an Array method.
foo_array.flatten!
or non destructive (just a return value foo_array remains unchanged)
foor_array.flatten
Simple, You should do like this
Test = [[{:id=>1, :name=>"Immad", :age=>18}], [{:id=>2, :name=>"Vicky", :age=>21}], [{:id=>3, :name=>"Adam", :age=>24}]]
Use .flatten here.
Test.flatten
=> [{:id=>1, :name=>"Immad", :age=>18}, {:id=>2, :name=>"Vicky", :age=>21}, {:id=>3, :name=>"Adam", :age=>24}]
Use .map!:
foo = [
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
# Call .map! on the array
foo.map! { |array| array.first }
=> [
[0] {
:id => 1,
:name => "Immad",
:age => 18
},
[1] {
:id => 2,
:name => "Vicky",
:age => 21
},
[2] {
:id => 3,
:name => "Adam",
:age => 24
}
]
A stream constains the following objects
const data = [
{ type: 'gps', id: 1, val: 1 },
{ type: 'gps', id: 2, val: 2 },
{ type: 'speed', id: 2, val: 3 },
{ type: 'gps', id: 3, val: 4 },
{ type: 'speed', id: 4, val: 5 },
{ type: 'gps', id: 4, val: 6 },
{ type: 'gps', id: 5, val: 7 }
]
In case the ids are the same, the objects are merged. If no id matches up, the object is ignored:
[
[{type: 'gps', id:2, val:2}, { type: 'speed', id: 2, val: 3 }],
[{ type: 'speed', id: 4, val: 5 },{ type: 'gps', id: 4, val: 6 }]
]
My idea was to group objects with the same type, ending up with two new streams
Rx.Observable.from(data)
.groupBy((x) => x.type)
.flatMap((g) => ...)
....
and then to merge/zip them again if the id is equal.
I'm not sure how to specify this in Rx and I'm also not sure if this is a good approach.
There is no need to split the stream and merge it back again. You can use scan to collect the objects and filter out the ones that do not meet to condition
const data = [
{ type: 'gps', id: 1, val: 1 },
{ type: 'gps', id: 2, val: 2 },
{ type: 'speed', id: 2, val: 3 },
{ type: 'gps', id: 3, val: 4 },
{ type: 'speed', id: 4, val: 5 },
{ type: 'gps', id: 4, val: 6 },
{ type: 'gps', id: 5, val: 7 }
]
const generator$ = Rx.Observable.from(data)
generator$
.scan((acc, x) => {
if (R.contains(x.id, R.pluck('id', acc))) {
acc.push(x);
} else {
acc = [x]
}
return acc
}, [])
.filter(x => x.length > 1)
.subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.min.js"></script>
I am looking to group a list of projects by a nested field, in this case custom_field.value when a certain id is passed in.
[{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
So if the params[:id] == 4 I want the list to be ordered by the custom_field id's corresponding value in decending order.
So in this case they would be ordered.
2012
2011
2010
Any ideas?
Is this what you are looking for? Your question is a bit unclear but I think this should suffice:
Your Original Hash:
test = [{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
Use group_by and sort(with handling for elements where there is no id found):
def group_and_sort(test_hash,id)
test_hash.group_by do |g|
elem = g[:custom_fields].detect {|h| h[:id] == id}
elem ? elem[:value] : "0"
end.sort.reverse.to_h
end
Then call like:
group_and_sort(test,4)
#=>{"2012"=>
[{:id=>3,
:name=>"project three ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2012"},
{:id=>5, :name=>"Priority", :value=>"high"}]}],
"2011"=>
[{:id=>2,
:name=>"project two ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2011"},
{:id=>5, :name=>"Priority", :value=>"medium"}]}],
"2010"=>
[{:id=>1,
:name=>"project one ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2010"},
{:id=>5, :name=>"Priority", :value=>"low"}]}]}
Assume projects is the data you pasted.
def order_values_by_id(pjs, id)
pjs.map{|p| p[:custom_fields].find{|f| f[:id] == id}[:value] }.sort.reverse
end
order_values_by_id(projects)
#=> ["2012", "2011", "2010"]
Let's say I have a table called bookshelf. I create a bookshelf document that has an array of IDs that references books in another table called books. I am trying to use getAll and merge to combine the documents in a query but I cannot seem to be able to pass all the elements into getAll. I believe getAll is interpreting the array of IDs as a single literal "object".
A bookshelf document:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ]
}
A book document:
{
id: 1
name: 'Merging in RethinkDB for Dummies'
}
My query:
r.db('test').table('bookshelf').merge(function(bookshelf) {
return {
booksOnShelf: r.table('books').getAll(bookshelf('books')).coerceTo('array')
}
})
Expected result:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ],
booksOnShelf: [
{ id: 1, name: 'Merging in RethinkDB for Dummies' },
{ id: 2, name: 'Cool Title!' },
...
]
}
Actual result:
{
id: 'MyShelf'
books: [ 1, 2, 6, 9 ],
booksOnShelf: [ ]
}
It turns out that I needed to use r.args to turn the array into actual arguments for use with the getAll function.
Here is the correct query:
r.db('test').table('bookshelf').merge(function(bookshelf) {
return {
booksOnShelf: r.table('books').getAll(r.args(bookshelf('books'))).coerceTo('array')
}
})
And the result:
{
"books": [ 1, 2 ],
"booksOnShelf": [{
"id": 1,
"title": "RethinkDB for Dummies"
}, {
"id": 2,
"title": "Awesome Title!"
}],
"id": "MyShelf"
}
By querying two classes and applying the following methods:
.each_with_index.chunk { |enum, i| enum.is_a?(Note) }
I get this structure:
[
[false, [[#<Post id: 1, title: "something", 0]]],
[false, [[#<Post id: 2, title: "something", 1], [#<Post id: 3, title: "something", 2]]],
[true, [[#<Note id: 1, title: "something", 3], [#<Note id: 77, title: "something", 4]]]
]
In the view I need to know when I'm on the last object, in this case index position 4, how do I do this?
In the view you could just do:
array.flatten.last
Which looks like this:
[1] pry(main)> [[false, [["1"]]], [false, [["2"], ["post 3"]]], [true, [["1"], ["note 77"]]]].flatten.last
=> "note 77"
Thanks Cary for the revision.