Completed
Push — master ( 64f3a1...18f77b )
by Pieter Epeüs
23s queued 11s
created

src/objects.js   A

Complexity

Total Complexity 28
Complexity/F 1.4

Size

Lines of Code 194
Function Count 20

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 28
eloc 64
mnd 8
bc 8
fnc 20
dl 0
loc 194
rs 10
bpm 0.4
cpm 1.4
noi 0
c 0
b 0
f 0
1
import { Validator } from '@hckrnews/validator';
2
3
/**
4
 * Object helper
5
 *
6
 * @param {object} options
7
 *
8
 * @return {class}
9
 */
10
const ObjectGenerator = options => {
11
    const schema = options?.schema;
12
    const validator = schema ? new Validator(schema) : null;
13
14
    return class Obj {
15
        /**
16
         * Set the original and prefix.
17
         *
18
         * @param {object} original
19
         * @param {string} prefix
20
         */
21
        constructor(original, prefix) {
22
            this.original = original;
23
            this.prefix = prefix;
24
            this.flatObject = {};
25
            if (validator) {
26
                this.validate();
27
            }
28
            this.parse();
29
        }
30
31
        validate() {
32
            if (!validator.validate(this.original)) {
33
                const [field, type] = validator.errors[0];
34
                if (type.constructor === String) {
35
                    throw new Error(`The field ${field} should be a ${type}`);
36
                } else {
37
                    throw new Error(
38
                        `The field ${field} should be a ${type.name}`
39
                    );
40
                }
41
            }
42
        }
43
44
        /**
45
         * flatten the object 1 level per time.
46
         */
47
        parse() {
48
            Object.entries(this.original).forEach(
49
                ([originalRowIndex, originalRow]) => {
50
                    let index = originalRowIndex;
51
52
                    if (this.prefix) {
53
                        index = [this.prefix, originalRowIndex].join('.');
54
                    }
55
56
                    if (typeof originalRow === 'object') {
57
                        const childRows = new Obj(originalRow, index).flat;
58
59
                        this.flatObject = Object.assign(
60
                            this.flatObject,
61
                            childRows
62
                        );
63
64
                        return;
65
                    }
66
67
                    this.flatObject[index] = originalRow;
68
                }
69
            );
70
        }
71
72
        /**
73
         * Get the flat object.
74
         *
75
         * @return {object}
76
         */
77
        get flat() {
78
            return this.flatObject;
79
        }
80
81
        /**
82
         * Get the object entries.
83
         *
84
         * @return {array}
85
         */
86
        entries() {
87
            return Object.entries(this.flatObject);
88
        }
89
90
        /**
91
         * Get the object keys.
92
         *
93
         * @return {array}
94
         */
95
        keys() {
96
            return Object.keys(this.flatObject);
97
        }
98
99
        /**
100
         * Get the object values.
101
         *
102
         * @return {array}
103
         */
104
        values() {
105
            return Object.values(this.flatObject);
106
        }
107
108
        /**
109
         * Get the object length.
110
         *
111
         * @return {number}
112
         */
113
        get length() {
114
            return Object.keys(this.flatObject).length;
115
        }
116
117
        /**
118
         * Get an item by key.
119
         *
120
         * @param {string} key
121
         * @param {object|null} defaultValue
122
         *
123
         * @return {object|null}
124
         */
125
        getByKey(key, defaultValue) {
126
            if (this.originalHas(key)) {
127
                return Object.getOwnPropertyDescriptor(this.original, key)
128
                    .value;
129
            }
130
131
            if (this.has(key)) {
132
                return this.flatObject[key];
133
            }
134
135
            if (this.includes(key)) {
136
                return this.entries()
137
                    .filter(([currentKey]) => currentKey.startsWith(key))
138
                    .reduce((accumulator, [currentKey, currentValue]) => {
139
                        const subKey = currentKey.substring(key.length + 1);
140
                        accumulator[subKey] = currentValue;
141
                        return accumulator;
142
                    }, {});
143
            }
144
145
            return defaultValue;
146
        }
147
148
        /**
149
         * Get keys of an item.
150
         *
151
         * @param {array} keys
152
         * @param {object|null} defaultValue
153
         *
154
         * @return {object|null}
155
         */
156
        getFlatKeys(keys, defaultValue) {
157
            const result = this.entries().filter(([currentKey]) =>
158
                keys.some(key => currentKey.startsWith(key))
159
            );
160
161
            if (result.length < 1) {
162
                return defaultValue;
163
            }
164
165
            return Object.fromEntries(result);
166
        }
167
168
        /**
169
         * Get keys of an item.
170
         *
171
         * @param {array} keys
172
         * @param {object|null} defaultValue
173
         *
174
         * @return {object|null}
175
         */
176
        getKeys(keys, defaultValue) {
177
            const result = keys.reduce((accumulator, currentKey) => {
178
                const key = currentKey.toString();
179
                const value = this.getByKey(key);
180
181
                if (value) {
182
                    accumulator[key] = value;
183
                }
184
185
                return accumulator;
186
            }, {});
187
188
            if (Object.keys(result).length < 1) {
189
                return defaultValue;
190
            }
191
192
            return result;
193
        }
194
195
        /**
196
         * Check if the original object has a key.
197
         *
198
         * @param {string} key
199
         *
200
         * @return {boolean}
201
         */
202
        originalHas(key) {
203
            return Object.prototype.hasOwnProperty.call(this.original, key);
204
        }
205
206
        /**
207
         * Check if the object has a key.
208
         *
209
         * @param {string} key
210
         *
211
         * @return {boolean}
212
         */
213
        has(key) {
214
            return Object.prototype.hasOwnProperty.call(this.flatObject, key);
215
        }
216
217
        /**
218
         * Check if the object has a key that includes.
219
         *
220
         * @param {string} key
221
         *
222
         * @return {boolean}
223
         */
224
        includes(key) {
225
            return this.keys().filter(item => item.startsWith(key)).length > 0;
226
        }
227
    };
228
};
229
230
export default ObjectGenerator;
231