I am trying to update a nested hashmap using update-in function. But I want to update value of two keys, using differents functions. For exemple:
I have this hash:
{:1 {:value 0, :active false}, :2 {:value 0, :active false}
And I want update the key :1 to:
{:1 {:value 2, :active true}, :2 {:value 0, :active false}
There is some way to do this ?
Thanks in advance
Update
Maybe I just can use assoc: (assoc my-map :1 {:value 2, :active true})
You can have more than one k/v pair with assoc:
user=> (def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})
#'user/m
user=> (update-in m [:1] assoc :value 1 :active true)
{:1 {:value 1, :active true}, :2 {:value 0, :active false}}
There are also assoc-in which works like assoc. The only difference is, that you provide a vector of keys instead of a single key. So maybe you can pipe your map through some assoc-in's.
Or you use the function update (added in 1.7):
(update {:1 {:value 0, :active false}, :2 {:value 0, :active false}
:1 (fn [{:keys [value active]]
(magic value active)))
Related
This is the code for my list:
Future<List<Map>> queryDb() async {
List localList;
final db = await database;
final allRows = await db.query(TABLE_FAVORITE);
localList = allRows.toList(growable: true);
localList.removeWhere((item) => item[COLUMN_ISFAVORITE] == 0);
publicFavoriteList = localList;
print(localList);
return localList;
}
whenever it gets called it prints:
[{id: 0, isFavorite: 1}, {id: 1, isFavorite: 1}, {id: 2, isFavorite: 1}, {id: 4, isFavorite: 1}]
How can I "cut" this to
[{0}, {1}, {2}, {4}]
Use map to extract the values from the inner Map objects:
var localValues = localList.map((o) => o['id'] as int).toList();
print(localVales);
// Prints: [0, 1, 2, 4]
I need to merge several hashes. I mean having:
a = {:x => 72, :y => 2}
b = {:x => 34, :y => 24}
c = a.deep_merge!(b)
and c is {:x => 106, :y => 26}.
Is there a feature in Crystal like deep_merge in Ruby?
EDIT : The real hash is more complex
{"request" => {"duration" => 15100012.0, "total" => 252562.0, "per_second" => 16725.0, "bytes" => 43440664.0}, "error" => {"socket" => 0.0, "read" => 25.0, "write" => 0.0, "http" => 0.0, "timeout" => 0.0}, "latency" => {"minimum" => 234.0, "maximum" => 219466.0, "average" => 7640.0, "deviation" => 12940.0}, "percentile" => {"fifty" => 2732.0, "ninety" => 20335.0, "ninety_nine" => 65824.0, "ninety_nine_ninety" => 199536.0}}
in fact it's a json transformed in a hash with PullParser
my final goal is to add create a new hash composed by values (added) from this hash on a loop
What you are describing is not a deep merge (a recursive operation), but just a custom merge strategy.
It can be solved by calling Hash#merge with a block:
a = {:x => 72, :y => 2}
b = {:x => 34, :y => 24}
c = a.merge(b) { |_, v1, v2| v1 + v2 }
pp c # => {:x => 106, :y => 26}
I'm trying to create an initializer for a Class that receives a Hash as parameter. The Hash is a {String => Type} hash, and can be nested. I'm getting an error when running this code:
#file: types.cr
class Types
alias Type = Nil |
Bool |
Int32 |
Int64 |
Float64 |
String |
Array(Type) |
Hash(String, Type)
def initialize(#input : Type)
end
end
input = {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}
s = Types.new(input)
Here is the error I get when running the code above:
$ crystal types.cr
Error in types.cr:16: instantiating 'Types:Class#new(Hash(String, Hash(String, Hash(String, Hash(String, Bool | Int32)))))'
s = Types.new(input)
^~~
in types.cr:11: instance variable '#input' of Types must be Types::Type, not Hash(String, Hash(String, Hash(String, Hash(String, Bool | Int32))))
def initialize(#input : Type)
^~~~~~
Is this possible with Crystal? How should I approach this?
Thanks!
You can do this specifying type of each hash:
c = {"c1" => 1, "c2" => 2, "c3" => true} of String => Types::Type
b = {"c" => c} of String => Types::Type
a = {"b" => b} of String => Types::Type
t = Types.new({"a" => a} of String => Types::Type)
pp t # => #<Types:0x103085ec0
# #input=
# {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}>
Another approach is to define and use Hash-like type:
alias Type = Nil |
Bool |
Int32 |
Int64 |
Float64 |
String |
Array(Type) |
Hash(String, Type)
alias TypesHash = Hash(String, Type)
t = TypesHash{
"a" => TypesHash{
"b" => TypesHash{
"c" => TypesHash{
"c1" => 1, "c2" => 2, "c3" => true,
},
},
},
}
t # {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}
t["a"] # {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}
t["a"].as(TypesHash)["b"] # {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}
t["a"].as(TypesHash)["b"].as(TypesHash)["c"] # {"c1" => 1, "c2" => 2, "c3" => true}
So you can pass it to the constructor just like TypesHash object:
class Types
def initialize(#input : TypesHash); end
end
Types.new t
I have two models,
class User < ActiveRecord::Base
has_many :posts, -> { order('post.id') }
end
class Post < ActiveRecord::Base
belongs_to: user
end
For instance i'm having a #user and two posts associated. while doing #user.posts, the result be like.
[
[0] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[1] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
}
]
Now, I'm building one more extra object by doing #user.posts.build and
that the below result of doing #user.posts
[
[0] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[1] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
},
[2] #<Post:0x0000000aa53a50> {
:id => nil,
:title => nil,
:comment => nil
},
]
What i actually want is, to sort by object with nil first. The result should exactly look like,
[
[0] #<Post:0x0000000aa53a50> {
:id => nil,
:title => nil,
:comment => nil
},
[1] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[2] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
}
]
It can also be done by an custom method to sort by looping through each object. But don't want to write another method. The result should in Association Proxy and not an Array
Is it possible to achieve it in association proxy itself?
Suppose, you have the #posts variable where it contains the nil item.
#posts.sort{|i,j| i.id && j.id ? i <=> j : j.id ? -1 : 1 }
result => [nil, 3, 5]
So, as a result of a database call, I get a vector of maps, which let's say look like this:
[{:make "vw", :model "vanagon", :color "blue", :year 1983}
{:make "vw", :model "vanagon", :color "red", :year 1987}
{:make "vw", :model "eurovan", :color "blue", :year 1995}
{:make "vw", :model "eurovan", :color "green", :year 1997}
{:make "geo", :model "metro", :color "blue", :year 1985}
{:make "geo", :model "metro", :color "yellow", :year 1994}]
How can I get that into a nested map using two of the fields, e.g. like this:
{"vw" {"vanagon" [{:color "blue", :year 1983}, {:color "red", :year 1987}]
"eurovan" [{:color "blue", :year 1995}, {:color "green", :year 1997}]}
"geo" {"metro" [{:color "blue", :year 1985}, {:color "yellow", :year 1994}]}}
I have been messing around with group-by and other coll functions for a couple hours, and I can't wrap my head around it and figure out a reasonable way to do this.
Thanks!
(reduce (fn [aggr {:keys [make model] :as row}]
(update-in aggr
[make model]
(fnil conj [])
(dissoc row :make :model)))
{} data)
The anonymous function does a destructuring bind. update-in rewrites bound structure. The basic idea is to use conj to add in the other elements of the row. fnil is there to specify that we want vectors (when nil is found use the empty vector as the 1st argument to conj). Results are combined in a map by reduce.