Skip to content

Commit 0f704aa

Browse files
committed
add object_to_map/1|2
1 parent 3059c2a commit 0f704aa

File tree

5 files changed

+132
-21
lines changed

5 files changed

+132
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
### Added
99
- Reimplement `String.split_at/2` to make sure Unicode library isn't compiled
1010
- Added `ElixirScript.JS.map_to_object/2` with options [keys: :string, symbols: false]
11+
- Added `ElixirScript.JS.object_to_map/1|2` with options [keys: :atom, recurse_array: true]
1112

1213
### Fixed
1314
- Make sure not to add underscores to erlang functions

lib/elixir_script/lib/js.ex

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ defmodule ElixirScript.JS do
6060
ElixirScript.JS.map_to_object(%{my: "map"})
6161
```
6262
"""
63-
defexternal map_to_object(object)
63+
defexternal map_to_object(map)
6464

6565
@doc """
6666
Takes the given map and returns an object
@@ -71,5 +71,26 @@ defmodule ElixirScript.JS do
7171
ElixirScript.JS.map_to_object(%{my: "map"}, keys: :string)
7272
```
7373
"""
74-
defexternal map_to_object(object, options)
74+
defexternal map_to_object(map, options)
75+
76+
@doc """
77+
Takes the given object and returns a map
78+
Options include [{:keys, :atom}, {:recurse_array, true}]
79+
80+
81+
```elixir
82+
ElixirScript.JS.object_to_object({my: "object"})
83+
```
84+
"""
85+
defexternal object_to_map(object)
86+
87+
@doc """
88+
Takes the given object and returns a map
89+
Options include [{:keys, :atom}, {:recurse_array, true}]
90+
91+
```elixir
92+
ElixirScript.JS.object_to_object({my: "map"}, keys: :atom)
93+
```
94+
"""
95+
defexternal object_to_map(object, options)
7596
end

lib/elixir_script/passes/translate/forms/js.ex

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,55 @@ defmodule ElixirScript.Translate.Forms.JS do
9191
{ast, state}
9292
end
9393

94-
def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [object]}, state) do
94+
def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [map]}, state) do
9595
ast = Helpers.call(
9696
J.member_expression(
9797
Helpers.functions(),
9898
J.identifier("map_to_object")
9999
),
100100
[
101-
Form.compile!(object, state)
101+
Form.compile!(map, state)
102102
]
103103
)
104104

105105
{ast, state}
106106
end
107107

108-
def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [object, options]}, state) do
108+
def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [map, options]}, state) do
109109
ast = Helpers.call(
110110
J.member_expression(
111111
Helpers.functions(),
112112
J.identifier("map_to_object")
113113
),
114+
[
115+
Form.compile!(map, state),
116+
Form.compile!(options, state)
117+
]
118+
)
119+
120+
{ast, state}
121+
end
122+
123+
def compile({{:., _, [ElixirScript.JS, :object_to_map]}, _, [object]}, state) do
124+
ast = Helpers.call(
125+
J.member_expression(
126+
Helpers.functions(),
127+
J.identifier("object_to_map")
128+
),
129+
[
130+
Form.compile!(object, state)
131+
]
132+
)
133+
134+
{ast, state}
135+
end
136+
137+
def compile({{:., _, [ElixirScript.JS, :object_to_map]}, _, [object, options]}, state) do
138+
ast = Helpers.call(
139+
J.member_expression(
140+
Helpers.functions(),
141+
J.identifier("object_to_map")
142+
),
114143
[
115144
Form.compile!(object, state),
116145
Form.compile!(options, state)

src/javascript/lib/core/functions.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,39 @@ function map_to_object(map, options = []) {
119119
return object;
120120
}
121121

122+
function object_to_map(object, options = []) {
123+
const opt_atom_keys = proplists.get_value(Symbol.for('keys'), options) === Symbol.for('atom');
124+
const opt_recurse_array = proplists.get_value(Symbol.for('recurse_array'), options) === true;
125+
126+
if (object.constructor === Object) {
127+
let map = new Map();
128+
Reflect.ownKeys(object).forEach(key => {
129+
let key2 = key;
130+
let value = object[key];
131+
if (opt_atom_keys && typeof key === 'string') {
132+
key2 = Symbol.for(key);
133+
}
134+
135+
if (value.constructor === Object || (value instanceof Array && opt_recurse_array)) {
136+
value = object_to_map(value, options);
137+
}
138+
map.set(key2, value);
139+
});
140+
return map;
141+
142+
} else if (object instanceof Array && opt_recurse_array) {
143+
return object.map(function(ele) {
144+
if (ele.constructor === Object || ele instanceof Array) {
145+
return object_to_map(ele, options);
146+
}
147+
return ele;
148+
});
149+
150+
} else {
151+
throw new Error(`Object ${object} is not an native object or array`);
152+
}
153+
}
154+
122155
class Recurse {
123156
constructor(func) {
124157
this.func = func;
@@ -168,6 +201,7 @@ export default {
168201
defimpl,
169202
build_namespace,
170203
map_to_object,
204+
object_to_map,
171205
trampoline,
172206
Recurse,
173207
split_at,

src/javascript/tests/core/functions.spec.js

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,54 @@ test('split_at', (t) => {
2323
t.deepEqual(Functions.split_at('😀abélkm', 4).values, ['😀abé', 'lkm']);
2424
});
2525

26-
//TODO: Fix these tests
27-
/*test('map_to_object/1', (t) => {
28-
const s_key = Symbol.for('key');
29-
const s_anotherKey = Symbol.for('anotherKey');
30-
31-
const map = new Map([[s_key, 'value'], [s_anotherKey, 'value2']]);
26+
test('map_to_object/1', (t) => {
27+
const map = new Map([[Symbol.for('key'), 'value'], [Symbol.for('anotherKey'), 'value2']]);
3228
const result = Functions.map_to_object(map);
33-
t.deepEqual(result, { s_key: 'value', s_anotherKey: 'value2' });
29+
const obj = {};
30+
obj[Symbol.for('key')] = 'value';
31+
obj[Symbol.for('anotherKey')] = 'value2';
32+
t.deepEqual(result, obj);
3433
});
3534

3635
test('map_to_object/2', (t) => {
37-
const s_key = Symbol.for('key');
38-
const s_anotherKey = Symbol.for('anotherKey');
39-
40-
const map = new Map([[s_key, 'value'], [s_anotherKey, 'value2']]);
41-
const options = [new Core.Tuple(Symbol.for('keys'), Symbol.for('strings'))];
36+
const map = new Map([[Symbol.for('key'), 'value'], [Symbol.for('anotherKey'), 'value2']]);
37+
const options = [new Core.Tuple(Symbol.for('keys'), Symbol.for('string'))];
4238
const result = Functions.map_to_object(map, options);
4339

4440
t.deepEqual(result, { key: 'value', anotherKey: 'value2' });
41+
});
42+
43+
test('object_to_map/1', (t) => {
44+
let obj = {};
45+
let result = Functions.object_to_map(obj);
46+
t.deepEqual(result, new Map());
47+
48+
obj = {key: 'value'};
49+
result = Functions.object_to_map(obj);
50+
t.deepEqual(result, new Map([['key', 'value']]));
51+
52+
obj = {};
53+
obj[Symbol.for('key')] = 'value';
54+
result = Functions.object_to_map(obj);
55+
t.deepEqual(result, new Map([[Symbol.for('key'), 'value']]));
56+
});
57+
58+
test('object_to_map/2', (t) => {
59+
let obj = {};
60+
let result = Functions.object_to_map(obj, []);
61+
t.deepEqual(result, new Map());
4562

46-
map = new Map([[s_key, 'value'], [s_anotherKey, 'value2']]);
47-
result = Functions.map_to_object(map, []);
63+
obj = {key: 'value'};
64+
result = Functions.object_to_map(obj, [new Core.Tuple(Symbol.for('keys'), Symbol.for('atom'))]);
65+
t.deepEqual(result, new Map([[Symbol.for('key'), 'value']]));
4866

49-
t.deepEqual(result, { s_key: 'value', s_anotherKey: 'value2' });
50-
});*/
67+
obj = {};
68+
obj[Symbol.for('key')] = [{nest1: 'valuenest1'},{nest2: 'valuenest2'}];
69+
result = Functions.object_to_map(obj, [
70+
new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')),
71+
new Core.Tuple(Symbol.for('recurse_array'), true)]);
72+
t.deepEqual(result, new Map([[Symbol.for('key'), [
73+
new Map([[Symbol.for('nest1'), 'valuenest1']]),
74+
new Map([[Symbol.for('nest2'), 'valuenest2']])
75+
]]]));
76+
});

0 commit comments

Comments
 (0)