UNPKG

7.19 kBJavaScriptView Raw
1'use strict'
2
3const clone = require('../../utils/object').clone
4const validateIndex = require('../../utils/array').validateIndex
5const getSafeProperty = require('../../utils/customs').getSafeProperty
6const setSafeProperty = require('../../utils/customs').setSafeProperty
7const DimensionError = require('../../error/DimensionError')
8
9function factory (type, config, load, typed) {
10 const matrix = load(require('../../type/matrix/function/matrix'))
11
12 /**
13 * Get or set a subset of a matrix or string.
14 *
15 * Syntax:
16 * math.subset(value, index) // retrieve a subset
17 * math.subset(value, index, replacement [, defaultValue]) // replace a subset
18 *
19 * Examples:
20 *
21 * // get a subset
22 * const d = [[1, 2], [3, 4]]
23 * math.subset(d, math.index(1, 0)) // returns 3
24 * math.subset(d, math.index([0, 1], 1)) // returns [[2], [4]]
25 *
26 * // replace a subset
27 * const e = []
28 * const f = math.subset(e, math.index(0, [0, 2]), [5, 6]) // f = [[5, 6]]
29 * const g = math.subset(f, math.index(1, 1), 7, 0) // g = [[5, 6], [0, 7]]
30 *
31 * See also:
32 *
33 * size, resize, squeeze, index
34 *
35 * @param {Array | Matrix | string} matrix An array, matrix, or string
36 * @param {Index} index An index containing ranges for each
37 * dimension
38 * @param {*} [replacement] An array, matrix, or scalar.
39 * If provided, the subset is replaced with replacement.
40 * If not provided, the subset is returned
41 * @param {*} [defaultValue=undefined] Default value, filled in on new entries when
42 * the matrix is resized. If not provided,
43 * math.matrix elements will be left undefined.
44 * @return {Array | Matrix | string} Either the retrieved subset or the updated matrix.
45 */
46 const subset = typed('subset', {
47 // get subset
48 'Array, Index': function (value, index) {
49 const m = matrix(value)
50 const subset = m.subset(index) // returns a Matrix
51 return index.isScalar()
52 ? subset
53 : subset.valueOf() // return an Array (like the input)
54 },
55
56 'Matrix, Index': function (value, index) {
57 return value.subset(index)
58 },
59
60 'Object, Index': _getObjectProperty,
61
62 'string, Index': _getSubstring,
63
64 // set subset
65 'Array, Index, any': function (value, index, replacement) {
66 return matrix(clone(value))
67 .subset(index, replacement, undefined)
68 .valueOf()
69 },
70
71 'Array, Index, any, any': function (value, index, replacement, defaultValue) {
72 return matrix(clone(value))
73 .subset(index, replacement, defaultValue)
74 .valueOf()
75 },
76
77 'Matrix, Index, any': function (value, index, replacement) {
78 return value.clone().subset(index, replacement)
79 },
80
81 'Matrix, Index, any, any': function (value, index, replacement, defaultValue) {
82 return value.clone().subset(index, replacement, defaultValue)
83 },
84
85 'string, Index, string': _setSubstring,
86 'string, Index, string, string': _setSubstring,
87 'Object, Index, any': _setObjectProperty
88 })
89
90 subset.toTex = undefined // use default template
91
92 return subset
93
94 /**
95 * Retrieve a subset of a string
96 * @param {string} str string from which to get a substring
97 * @param {Index} index An index containing ranges for each dimension
98 * @returns {string} substring
99 * @private
100 */
101 function _getSubstring (str, index) {
102 if (!type.isIndex(index)) {
103 // TODO: better error message
104 throw new TypeError('Index expected')
105 }
106 if (index.size().length !== 1) {
107 throw new DimensionError(index.size().length, 1)
108 }
109
110 // validate whether the range is out of range
111 const strLen = str.length
112 validateIndex(index.min()[0], strLen)
113 validateIndex(index.max()[0], strLen)
114
115 const range = index.dimension(0)
116
117 let substr = ''
118 range.forEach(function (v) {
119 substr += str.charAt(v)
120 })
121
122 return substr
123 }
124
125 /**
126 * Replace a substring in a string
127 * @param {string} str string to be replaced
128 * @param {Index} index An index containing ranges for each dimension
129 * @param {string} replacement Replacement string
130 * @param {string} [defaultValue] Default value to be uses when resizing
131 * the string. is ' ' by default
132 * @returns {string} result
133 * @private
134 */
135 function _setSubstring (str, index, replacement, defaultValue) {
136 if (!index || index.isIndex !== true) {
137 // TODO: better error message
138 throw new TypeError('Index expected')
139 }
140 if (index.size().length !== 1) {
141 throw new DimensionError(index.size().length, 1)
142 }
143 if (defaultValue !== undefined) {
144 if (typeof defaultValue !== 'string' || defaultValue.length !== 1) {
145 throw new TypeError('Single character expected as defaultValue')
146 }
147 } else {
148 defaultValue = ' '
149 }
150
151 const range = index.dimension(0)
152 const len = range.size()[0]
153
154 if (len !== replacement.length) {
155 throw new DimensionError(range.size()[0], replacement.length)
156 }
157
158 // validate whether the range is out of range
159 const strLen = str.length
160 validateIndex(index.min()[0])
161 validateIndex(index.max()[0])
162
163 // copy the string into an array with characters
164 const chars = []
165 for (let i = 0; i < strLen; i++) {
166 chars[i] = str.charAt(i)
167 }
168
169 range.forEach(function (v, i) {
170 chars[v] = replacement.charAt(i[0])
171 })
172
173 // initialize undefined characters with a space
174 if (chars.length > strLen) {
175 for (let i = strLen - 1, len = chars.length; i < len; i++) {
176 if (!chars[i]) {
177 chars[i] = defaultValue
178 }
179 }
180 }
181
182 return chars.join('')
183 }
184}
185
186/**
187 * Retrieve a property from an object
188 * @param {Object} object
189 * @param {Index} index
190 * @return {*} Returns the value of the property
191 * @private
192 */
193function _getObjectProperty (object, index) {
194 if (index.size().length !== 1) {
195 throw new DimensionError(index.size(), 1)
196 }
197
198 const key = index.dimension(0)
199 if (typeof key !== 'string') {
200 throw new TypeError('String expected as index to retrieve an object property')
201 }
202
203 return getSafeProperty(object, key)
204}
205
206/**
207 * Set a property on an object
208 * @param {Object} object
209 * @param {Index} index
210 * @param {*} replacement
211 * @return {*} Returns the updated object
212 * @private
213 */
214function _setObjectProperty (object, index, replacement) {
215 if (index.size().length !== 1) {
216 throw new DimensionError(index.size(), 1)
217 }
218
219 const key = index.dimension(0)
220 if (typeof key !== 'string') {
221 throw new TypeError('String expected as index to retrieve an object property')
222 }
223
224 // clone the object, and apply the property to the clone
225 const updated = clone(object)
226 setSafeProperty(updated, key, replacement)
227
228 return updated
229}
230
231exports.name = 'subset'
232exports.factory = factory