hash_unnest 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +26 -0
- data/ext/hash_unnest/extconf.rb +12 -0
- data/ext/hash_unnest/hash_unnest.c +167 -0
- data/ext/hash_unnest/hash_unnest.h +18 -0
- data/lib/hash_unnest.rb +7 -0
- data/lib/hash_unnest/version.rb +4 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c988aea31df14ef50afcaf73818f844a0ab61aca55fcf0614425a95441a6e810
|
4
|
+
data.tar.gz: 0f47f4110f2ee9a2ba2bd1133d8d55ec0c7c44135b569abc3efae44f6a18790f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d75dfe4e4d93a2ba794d0b0d4cfac7120aca446372063af6e012dee0ffcfcfd6a8ece663394359d4b273dac3ef74f7f039ac0a4b28d80767fa0c1264047b3bac
|
7
|
+
data.tar.gz: 98252c49b5744768268406c302441eb3654daf8c120cfb9d8ad605df97d22176591a20000616ec46afca8b933517a17bbcf565f15c90cdc5719a16af35438dc6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 HouseTrip Ltd.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# hash-unnest
|
2
|
+
|
3
|
+
Un-nests hashes, ie transforms:
|
4
|
+
|
5
|
+
{ a: { b: 1 }, c: 2 }
|
6
|
+
|
7
|
+
into
|
8
|
+
|
9
|
+
{ a.b: 1, c: 2 }
|
10
|
+
|
11
|
+
Keys in the output hash will be sorted in lexicographical order.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
gem install hash_unnest
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'hash_unnest'
|
21
|
+
Hash.include(HashUnnest)
|
22
|
+
|
23
|
+
h = { a: { b: 1 }, c: 2 }
|
24
|
+
h.unnest
|
25
|
+
# => { a.b: 1, c: 2 }
|
26
|
+
```
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
PLATFORM = `uname`.strip.upcase
|
4
|
+
SHARED_FLAGS = "--std=c99 -Wall -Wextra -Werror"
|
5
|
+
|
6
|
+
# production
|
7
|
+
$CFLAGS += " #{SHARED_FLAGS} -Os"
|
8
|
+
|
9
|
+
# development
|
10
|
+
# $CFLAGS += " #{SHARED_FLAGS} -O0 -g -DDEBUG"
|
11
|
+
|
12
|
+
create_makefile('hash_unnest/hash_unnest')
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <assert.h>
|
3
|
+
#include "hash_unnest.h"
|
4
|
+
|
5
|
+
// forward declarataions of closures
|
6
|
+
static int hash_unnest_size_closure(VALUE key, VALUE val, VALUE in);
|
7
|
+
static int hash_unnest_closure(VALUE key, VALUE val, VALUE in);
|
8
|
+
|
9
|
+
static VALUE eHashUnnestModule = Qnil;
|
10
|
+
|
11
|
+
// a (sortable) key-value entry
|
12
|
+
typedef struct {
|
13
|
+
VALUE key;
|
14
|
+
VALUE value;
|
15
|
+
} hn_record_t;
|
16
|
+
|
17
|
+
/******************************************************************************/
|
18
|
+
|
19
|
+
// Unnesting callback:
|
20
|
+
//
|
21
|
+
// - When `val` is anything but a hash, just add it to the `in[1]` buffer,
|
22
|
+
// using the prefix in `in[0]`.
|
23
|
+
//
|
24
|
+
// Note that in[1] is a pointer to the current buffer pointer;
|
25
|
+
// so it sets the buffer cell, thenmoves the pointer to the next cell.
|
26
|
+
//
|
27
|
+
// - When `val` is a hash, call ourselves recursively, appending the
|
28
|
+
// current key to the prefix.
|
29
|
+
//
|
30
|
+
static int hash_unnest_closure(VALUE key, VALUE val, VALUE in)
|
31
|
+
{
|
32
|
+
VALUE prefix = rb_ary_entry(in, 0);
|
33
|
+
hn_record_t** output = (hn_record_t**) rb_ary_entry(in, 1);
|
34
|
+
|
35
|
+
#ifdef DEBUG
|
36
|
+
VALUE _str_key = rb_funcall(key, rb_intern("inspect"), 0);
|
37
|
+
VALUE _str_val = rb_funcall(val, rb_intern("inspect"), 0);
|
38
|
+
LOG("*** hash_unnest_closure (%s,%s,'%s')\n", StringValueCStr(_str_key), StringValueCStr(_str_val), StringValueCStr(prefix));
|
39
|
+
#endif
|
40
|
+
|
41
|
+
switch(TYPE(val)) {
|
42
|
+
case T_HASH:
|
43
|
+
{
|
44
|
+
VALUE new_prefix;
|
45
|
+
VALUE new_in;
|
46
|
+
|
47
|
+
new_prefix = rb_str_dup(prefix);
|
48
|
+
rb_str_append(new_prefix, key);
|
49
|
+
rb_str_cat2(new_prefix, ".");
|
50
|
+
|
51
|
+
new_in = rb_ary_new2(2);
|
52
|
+
rb_ary_store(new_in, 0, new_prefix);
|
53
|
+
rb_ary_store(new_in, 1, (VALUE) output);
|
54
|
+
|
55
|
+
rb_hash_foreach(val, hash_unnest_closure, new_in);
|
56
|
+
break;
|
57
|
+
}
|
58
|
+
default:
|
59
|
+
{
|
60
|
+
VALUE new_key = rb_str_dup(prefix);
|
61
|
+
|
62
|
+
rb_str_append(new_key, key);
|
63
|
+
(*output)->key = new_key;
|
64
|
+
(*output)->value = val;
|
65
|
+
*output = *output + 1;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
return ST_CONTINUE;
|
69
|
+
}
|
70
|
+
|
71
|
+
/******************************************************************************/
|
72
|
+
|
73
|
+
// Recursively counts the number of leaves ("size") of a nested hash.
|
74
|
+
static int hash_unnest_size_closure(VALUE key, VALUE val, VALUE in)
|
75
|
+
{
|
76
|
+
switch(TYPE(val)) {
|
77
|
+
case T_HASH:
|
78
|
+
{
|
79
|
+
rb_hash_foreach(val, hash_unnest_size_closure, in);
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
default:
|
83
|
+
{
|
84
|
+
int* size = (int*) in;
|
85
|
+
*size = *size + 1;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return ST_CONTINUE;
|
89
|
+
}
|
90
|
+
|
91
|
+
/******************************************************************************/
|
92
|
+
|
93
|
+
// Compares the `key`s in `hn_record_t`s, for sorting purposes.
|
94
|
+
int hn_record_compare(const void* a, const void* b) {
|
95
|
+
hn_record_t* record_a = (hn_record_t*) a;
|
96
|
+
hn_record_t* record_b = (hn_record_t*) b;
|
97
|
+
|
98
|
+
LOG("compare '%s' to '%s'\n", StringValueCStr(record_a->key), StringValueCStr(record_b->key));
|
99
|
+
|
100
|
+
// NOTE: use StringValue + memcmp if slow
|
101
|
+
return strcmp(
|
102
|
+
StringValueCStr(record_a->key),
|
103
|
+
StringValueCStr(record_b->key)
|
104
|
+
);
|
105
|
+
}
|
106
|
+
|
107
|
+
/******************************************************************************/
|
108
|
+
|
109
|
+
static VALUE hash_unnest_unnest(VALUE self)
|
110
|
+
{
|
111
|
+
VALUE result, in, prefix;
|
112
|
+
hn_record_t* buf;
|
113
|
+
hn_record_t* buf_ptr;
|
114
|
+
int size = 0;
|
115
|
+
|
116
|
+
#ifdef DEBUG
|
117
|
+
{
|
118
|
+
VALUE _str_self = rb_funcall(self, rb_intern("to_s"), 0);
|
119
|
+
LOG("*** hash_unnest_unnest (%s)\n", StringValueCStr(_str_self));
|
120
|
+
}
|
121
|
+
#endif
|
122
|
+
|
123
|
+
rb_hash_foreach(self, hash_unnest_size_closure, (VALUE) &size);
|
124
|
+
#ifdef DEBUG
|
125
|
+
{
|
126
|
+
VALUE _str_self = rb_funcall(self, rb_intern("to_s"), 0);
|
127
|
+
LOG("*** size(%s): %d\n", StringValueCStr(_str_self), size);
|
128
|
+
}
|
129
|
+
#endif
|
130
|
+
|
131
|
+
buf = (hn_record_t*) malloc(size * sizeof(hn_record_t));
|
132
|
+
buf_ptr = buf;
|
133
|
+
|
134
|
+
prefix = rb_str_new("", 0);
|
135
|
+
|
136
|
+
in = rb_ary_new2(2);
|
137
|
+
rb_ary_store(in, 0, prefix);
|
138
|
+
rb_ary_store(in, 1, (VALUE) &buf_ptr);
|
139
|
+
|
140
|
+
rb_hash_foreach(self, hash_unnest_closure, in);
|
141
|
+
|
142
|
+
qsort((void*) buf, size, sizeof(hn_record_t), hn_record_compare);
|
143
|
+
|
144
|
+
result = rb_hash_new();
|
145
|
+
for (int k = 0; k < size; ++k) {
|
146
|
+
hn_record_t entry = buf[k];
|
147
|
+
|
148
|
+
rb_hash_aset(result, entry.key, entry.value);
|
149
|
+
}
|
150
|
+
|
151
|
+
free(buf);
|
152
|
+
|
153
|
+
return result;
|
154
|
+
}
|
155
|
+
|
156
|
+
/******************************************************************************/
|
157
|
+
|
158
|
+
void Init_hash_unnest(void) {
|
159
|
+
LOG("*** Init_hash_unnest\n");
|
160
|
+
|
161
|
+
/* assume we haven't yet defined hash_unnest */
|
162
|
+
eHashUnnestModule = rb_define_module("HashUnnest");
|
163
|
+
assert(eHashUnnestModule != Qnil);
|
164
|
+
|
165
|
+
rb_define_method(eHashUnnestModule, "unnest_c", hash_unnest_unnest, 0);
|
166
|
+
return;
|
167
|
+
}
|
data/lib/hash_unnest.rb
ADDED
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hash_unnest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julien Letessier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Fast hash unnesting
|
70
|
+
email:
|
71
|
+
- julien.letessier@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions:
|
74
|
+
- ext/hash_unnest/extconf.rb
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE.txt
|
78
|
+
- README.md
|
79
|
+
- ext/hash_unnest/extconf.rb
|
80
|
+
- ext/hash_unnest/hash_unnest.c
|
81
|
+
- ext/hash_unnest/hash_unnest.h
|
82
|
+
- lib/hash_unnest.rb
|
83
|
+
- lib/hash_unnest/version.rb
|
84
|
+
homepage: http://github.com/mezis/hash_unnest
|
85
|
+
licenses: []
|
86
|
+
metadata: {}
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 2.7.6
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Fast hash unnesting
|
107
|
+
test_files: []
|