Convert a hash into a struct
If it doesn't specifically have to be a Struct
and instead can be an OpenStruct
:
pry(main)> require 'ostruct'
pry(main)> s = OpenStruct.new(h)
=> #<OpenStruct a=1, b=2>
pry(main)> puts s.a, s.b
1
2
Convert Hash to OpenStruct recursively
personally I use the recursive-open-struct
gem - it's then as simple as RecursiveOpenStruct.new(<nested_hash>)
But for the sake of recursion practice, I'll show you a fresh solution:
require 'ostruct'
def to_recursive_ostruct(hash)
result = hash.each_with_object({}) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
OpenStruct.new(result)
end
puts to_recursive_ostruct(a: { b: 1}).a.b
# => 1
edit
Weihang Jian showed a slight improvement to this here https://stackoverflow.com/a/69311716/2981429
def to_recursive_ostruct(hash)
hash.each_with_object(OpenStruct.new) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
end
Also see https://stackoverflow.com/a/63264908/2981429 which shows how to handle arrays
note
the reason this is better than the JSON-based solutions is because you can lose some data when you convert to JSON. For example if you convert a Time object to JSON and then parse it, it will be a string. There are many other examples of this:
class Foo; end
JSON.parse({obj: Foo.new}.to_json)["obj"]
# => "#<Foo:0x00007fc8720198b0>"
yeah ... not super useful. You've completely lost your reference to the actual instance.
Convert array of hashes to array of structs?
Try this:
class User
attr_accessor :first_name
attr_accessor :last_name
end
class Race
attr_accessor :course
attr_accessor :start_time
attr_accessor :end_time
end
UserRace = Struct.new(:first_name, :last_name, :course, :start_time, :end_time)
def get_user_race_info
user_races = races.map do |r|
UserRace.new(r.user.first_name, r.user.last_name,
r.course, r.start_time, r.end_time)
end
end
Now let's test the result:
user_races = get_user_race_info
user_races[0].first_name
user_races[0].end_time
Ruby: Convert nested hash to object?
You need to add recursivity:
class Hashit
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v.is_a?(Hash) ? Hashit.new(v) : v)
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
end
end
h = Hashit.new({a: '123r', b: {c: 'sdvs'}})
# => #<Hashit:0x007fa6029f4f70 @a="123r", @b=#<Hashit:0x007fa6029f4d18 @c="sdvs">>
returning struct data as a hash in ruby
(Ruby <= 1.9.3) OpenStruct
has OpenStruct#marshall_dump and Struct
has Struct#each_pair (use to_a
to get the pairs and Hash
+to_a
to get the hash):
Person = Struct.new(:name, :age)
person = Person.new("Jamie", 23)
person_hash = Hash[person.each_pair.to_a]
#=> {:age=>23, :name=>"Jamie"}
With Ruby 2.0 things are easier: Struct#to_h, OpenStruct#to_h:
Person = Struct.new(:name, :age)
person = Person.new("Jamie", 23)
person_hash = person.to_h
#=> {:age=>23, :name=>"Jamie"}
Defining hash function as part of a struct
std::unordered_set
is defined within std
namespace. And it uses std::hash
structures to hash many different types. If you want to be able to use std::unordered_set<X>
(without adding much info to the declaration), you must create another overload of the std::hash
template, so as to make it hash your structure.
You should be able to get it working by doing the following:
# include <unordered_set>
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
namespace std {
template<>
struct hash<X> {
inline size_t operator()(const X& x) const {
// size_t value = your hash computations over x
return value;
}
};
}
int main() {
std::unordered_set<X> s;
}
Andalso, you must provide either an overload to std::equal_to
, or a comparison operator (operator==()
) for your structure. You should add one of the following:
struct X {
...
inline bool operator==(const X& other) const {
// bool comparison = result of comparing 'this' to 'other'
return comparison;
}
};
Or:
template <>
struct equal_to<X> {
inline bool operator()(const X& a, const X& b) const {
// bool comparison = result of comparing 'a' to 'b'
return comparison;
}
};
uthash adding a new entry to a struct, struct hashmap
This is the simplest modification I can come up with. Changes from the original are:
Change the first parameter of
add_entry
fromhash_map_entry *map
tohash_map_entry **map
and adjust the code accordingly.Use
HASH_ADD_KEYPTR
to hash the contents of the string insidestruct hash_ptr
instead of hashing thestruct hash_ptr
itself. NOTE: the code assumes that thelen
member is the length of the the object pointed to by thestring
member, in bytes.Related to 2, change the usage of
HASH_FIND
to hash the contents of the string insidestruct hash_ptr
.
Here is the result:
#include <stdio.h>
#include <stdlib.h>
#include "uthash.h"
typedef struct hash_ptr {
char* string;
size_t len;
}hash_ptr;
typedef struct hash_map_entry {
struct hash_ptr *key;
struct hash_ptr *value;
UT_hash_handle hh;
}hash_map_entry;
void add_entry(hash_map_entry **map, hash_ptr *key, hash_ptr *value) {
hash_map_entry *entry;
HASH_FIND(hh, *map, key->string, key->len, entry);
if (entry == NULL) {
entry = (hash_map_entry*) malloc(sizeof *entry);
memset(entry, 0, sizeof *entry);
entry->value = value;
entry->key = key;
HASH_ADD_KEYPTR(hh, *map, key->string, key->len, entry);
}
}
int main(void)
{
hash_map_entry *map = NULL;
hash_ptr *key = (hash_ptr*) malloc(sizeof *key);
memset(key, 0, sizeof *key);
key->string = "Is this the Krusty Krab?";
key->len = strlen(key->string);
hash_ptr *value = (hash_ptr*) malloc(sizeof *value);
memset(value, 0, sizeof *value);
value->string = "No, this is Patrick!";
value->len = strlen(value->string);
add_entry(&map, key, value);
hash_map_entry *find_me;
HASH_FIND(hh, map, key->string, key->len, find_me);
if (find_me)
{
printf("found key=\"%s\", val=\"%s\"\n", find_me->key->string, find_me->value->string);
}
else
{
printf("not found\n");
}
return 0;
}
Hash of nested (struct) data
In order to create a hash from the struct type column, you first need to convert the struct to e.g. string. to_json
does the job. After that you can use a hash function like md5
.
F.md5(F.to_json('name'))
Using your example df:
df = df.withColumn('md5', F.md5(F.to_json('name')))
df.show(truncate=0)
# +----------------------+-----+------+--------------------------------+
# |name |state|gender|md5 |
# +----------------------+-----+------+--------------------------------+
# |{James, null, Smith} |OH |M |ad4f22b4a03070026957a65b3b8e5bf9|
# |{Anna, Rose, } |NY |F |c8dcb8f6f52c2e382c33bd92819cd500|
# |{Julia, , Williams} |OH |F |63a7c53d21f53e37b3724312b14a8e97|
# |{Maria, Anne, Jones} |NY |M |a0f2d3962be4941828a2b6f4a02d0ac5|
# |{Jen, Mary, Brown} |NY |M |cae64ee19dd2a0c9745a20e759a527e9|
# |{Mike, Mary, Williams}|OH |M |5e882c033be16bd679f450889e97be6d|
# +----------------------+-----+------+--------------------------------+
Related Topics
Ruby: Module, Require and Include
Rails: Testing Named Scopes with Rspec
Setting Request Headers in Ruby
Installing Rails: "File Not Found: Lib"
Ruby: Finding Most Recently Modified File
Copying Gems from Previous Version of Ruby in Rbenv
In Ruby, When Should You Use Self. in Your Classes
How to Convert an Array of Strings into a Comma-Separated String
What Is the Meaning of "H" in "<%=H [ ...] %>"
How to Find the Source File for a Rake Task
Rspec: How to Test File Operations and File Content
How to Reference a File from Inside of a Gem