Convert a Hash into a Struct

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:

  1. Change the first parameter of add_entry from hash_map_entry *map to hash_map_entry **map and adjust the code accordingly.

  2. Use HASH_ADD_KEYPTR to hash the contents of the string inside struct hash_ptr instead of hashing the struct hash_ptr itself. NOTE: the code assumes that the len member is the length of the the object pointed to by the string member, in bytes.

  3. Related to 2, change the usage of HASH_FIND to hash the contents of the string inside struct 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



Leave a reply



Submit