UNPKG

6.92 kBJavaScriptView Raw
1import { isIndex } from '../../utils/is'
2import { clone } from '../../utils/object'
3import { validateIndex } from '../../utils/array'
4import { getSafeProperty, setSafeProperty } from '../../utils/customs'
5import { DimensionError } from '../../error/DimensionError'
6import { factory } from '../../utils/factory'
7
8const name = 'subset'
9const dependencies = ['typed', 'matrix']
10
11export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix }) => {
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 return typed(name, {
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
91/**
92 * Retrieve a subset of a string
93 * @param {string} str string from which to get a substring
94 * @param {Index} index An index containing ranges for each dimension
95 * @returns {string} substring
96 * @private
97 */
98function _getSubstring (str, index) {
99 if (!isIndex(index)) {
100 // TODO: better error message
101 throw new TypeError('Index expected')
102 }
103 if (index.size().length !== 1) {
104 throw new DimensionError(index.size().length, 1)
105 }
106
107 // validate whether the range is out of range
108 const strLen = str.length
109 validateIndex(index.min()[0], strLen)
110 validateIndex(index.max()[0], strLen)
111
112 const range = index.dimension(0)
113
114 let substr = ''
115 range.forEach(function (v) {
116 substr += str.charAt(v)
117 })
118
119 return substr
120}
121
122/**
123 * Replace a substring in a string
124 * @param {string} str string to be replaced
125 * @param {Index} index An index containing ranges for each dimension
126 * @param {string} replacement Replacement string
127 * @param {string} [defaultValue] Default value to be uses when resizing
128 * the string. is ' ' by default
129 * @returns {string} result
130 * @private
131 */
132function _setSubstring (str, index, replacement, defaultValue) {
133 if (!index || index.isIndex !== true) {
134 // TODO: better error message
135 throw new TypeError('Index expected')
136 }
137 if (index.size().length !== 1) {
138 throw new DimensionError(index.size().length, 1)
139 }
140 if (defaultValue !== undefined) {
141 if (typeof defaultValue !== 'string' || defaultValue.length !== 1) {
142 throw new TypeError('Single character expected as defaultValue')
143 }
144 } else {
145 defaultValue = ' '
146 }
147
148 const range = index.dimension(0)
149 const len = range.size()[0]
150
151 if (len !== replacement.length) {
152 throw new DimensionError(range.size()[0], replacement.length)
153 }
154
155 // validate whether the range is out of range
156 const strLen = str.length
157 validateIndex(index.min()[0])
158 validateIndex(index.max()[0])
159
160 // copy the string into an array with characters
161 const chars = []
162 for (let i = 0; i < strLen; i++) {
163 chars[i] = str.charAt(i)
164 }
165
166 range.forEach(function (v, i) {
167 chars[v] = replacement.charAt(i[0])
168 })
169
170 // initialize undefined characters with a space
171 if (chars.length > strLen) {
172 for (let i = strLen - 1, len = chars.length; i < len; i++) {
173 if (!chars[i]) {
174 chars[i] = defaultValue
175 }
176 }
177 }
178
179 return chars.join('')
180}
181
182/**
183 * Retrieve a property from an object
184 * @param {Object} object
185 * @param {Index} index
186 * @return {*} Returns the value of the property
187 * @private
188 */
189function _getObjectProperty (object, index) {
190 if (index.size().length !== 1) {
191 throw new DimensionError(index.size(), 1)
192 }
193
194 const key = index.dimension(0)
195 if (typeof key !== 'string') {
196 throw new TypeError('String expected as index to retrieve an object property')
197 }
198
199 return getSafeProperty(object, key)
200}
201
202/**
203 * Set a property on an object
204 * @param {Object} object
205 * @param {Index} index
206 * @param {*} replacement
207 * @return {*} Returns the updated object
208 * @private
209 */
210function _setObjectProperty (object, index, replacement) {
211 if (index.size().length !== 1) {
212 throw new DimensionError(index.size(), 1)
213 }
214
215 const key = index.dimension(0)
216 if (typeof key !== 'string') {
217 throw new TypeError('String expected as index to retrieve an object property')
218 }
219
220 // clone the object, and apply the property to the clone
221 const updated = clone(object)
222 setSafeProperty(updated, key, replacement)
223
224 return updated
225}