I've tried and surprised how could not I do with ExtJS. Let me explain with a code block.
In jQuery
console.clear();
var a = {
b: 5,
c: 4,
o: {
l: 2,
p: 2
}
}
var b = {
k: 4,
l: 3,
c: 5,
o: {
m: 2,
l: 1
}
}
var ex = $.extend(true, a, b);
console.dir(ex)
Here is the output
ex = {
a: {
q: 2
},
b: 5,
c: 5,
o: {
l: 1,
p: 2,
m: 2
}
}
Ext apply, applyIf, copyTo does not worked like this. How can I produce the output in ExtJS?
Thanks in advance.
For a recent project, we adapted this sample code to produce the following method:
Ext.deepCopy = function(p, c) {
c = c || (p.constructor === Array ? [] : {});
for (var i in p) {
if (typeof p[i] === 'object' && p[i] !== null) {
c[i] = p[i].constructor === Array ? [] : {};
Ext.deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
};
Deep copying isn't supported in Ext. There are Ext.apply and Ext.applyIf but they both only work on the first level of a hash map and will override instead of merge any embedded arrays or hashes.
In fact the docs explicitly state that Ext.apply is meant to work on config objects, not that it matters but it's just to illustrate that it's not meant to be used as a merge utility function although it basically could if you only want to merge the first level/depth.
Use the Ext.Object.merge() method, that does exactly what you're looking for.
Related
Let's say I have two objects.
obj1 = {
a: 1,
b: 3,
c: 1
}
obj2 = {
a: 3,
b: 1,
c: 3
}
And I would like to add these two. In a way that each key value add them together with the corresponding one. So obj3 = obj1 + obj2 would result in:
obj3 = {
a: 4,
b: 4,
c: 4
}
Is there a simpler way of doing this without manually adding each key value pair like
obj3.a = obj1.a + obj2.a
This is something that could be done very simply through the use of Array.prototype.reduce() if you were able to store these objects as an array:
const myObjects = [
{
a: 1,
b: 3,
c: 1,
},
{
a: 3,
b: 1,
c: 3,
},
];
const output = myObjects.reduce((prev, curr) => ({
a: prev.a + curr.a,
b: prev.b + curr.b,
c: prev.c + curr.c,
}));
This function sum property's object (dynamic property)
function objSumProperty(obj1, obj2) {
const data = {};
Object.keys(obj1).map((k) => (data[k] = obj1[k] + (obj2[k] || 0)));
return data;
}
Use parameters rest (...) to collect all objects to an array. Create an array of all entries ([key, value] pairs) using Array.flatMap() with Object.entries(). Reduce the array of entries to a single object.
This function allows you to combine all keys from 2 or more objects, even if the objects have different keys.
const fn = (...objs) => objs
.flatMap(Object.entries)
.reduce((acc, [key, value]) => ({
...acc,
[key]: (acc[key] ?? 0) + value
}), {})
const obj1 = {"a":1,"b":3,"c":1}
const obj2 = {"a":3,"b":1,"c":3}
console.log(fn(obj1, obj2)) // combining 2 objects
const obj3 = {"a":10,"d":15,"e":20}
console.log(fn(obj1, obj2, obj3)) // combining 3 objects
My Problem
Consider a nested object:
> { a: { b: { c: 3 } } }
{ a: { b: { c: 3 } } }
Accessing an inner property with a dot notation for fixed values is done using a dot notation:
> x.a.b.c
3
I would like to access an arbitrary property depending on some condition, for example, instead of accessing b I would like to access the property who's name is stored in the SOME_VARIABLE variable:
> x.a.{SOME_VARIABLE}.c
3
What have I tried
STFW. Probably don't know the exact terminology.
My question
How can I refer to an object property dynamically, with a property name defined in a variable?
There are multiple ways to access an object one of them being [] instead of dots if a object is var object = { inside : '1' } you can access it like this object['inside']. Remember to pass quotes inside if it's static and if it's dynamic pass the variable
I've added an example below
var a = { b: { c: 1 } };
var d = 'b';
console.log(a[d]['c']);
You might also consider using a library like lodash which provides functions to "reach inside" a complex object and return a value, of if the path doesn't exist, a default value.
Example:
const _ = require('lodash')
const target = {
foo: {
bar: {
baz: [1, 2, 3]
}
}
}
console.log(_.get(target, 'foo.bar.baz.1')) // ==> 2
console.log(_.get(target, 'foo.3.bar', 'DEFAULT')) // ==> DEFAULT
if (_.has(target, 'foo.bar')) {
// do something interesting
}
const newKey = 'blorg'
_.put(target, `foo.bar.${newKey}`, 'hello?')
/*
target is now {
foo: {
bar: {
baz: [1, 2, 3]
},
blorg: 'hello?'
}
}
*/
I have a multidimensional object and using Vue, I am trying to make the inner object reactive.
My object looks like this:
data() {
return {
myObject: {}
}
}
And the filled data looks like this:
myObject: {
1: { // (client)
0: "X", // (index) : (value)
1: "Y"
},
2: {
0: "A",
2: "B"
}
}
If I try using:
let value = "X";
let client = 1;
let index = 1;
let obj = {};
obj[client][index] = value;
this.myObject = Object.assign({}, this.myObject, obj);
It throws an error:
TypeError: Cannot set property '0' of undefined
And if I try below, it overwrites the initial values as it is initially setting the object to {}
let obj = {};
obj[index] = value;
let parentObj = {};
parentObj[client] = obj;
this.myObject = Object.assign({}, this.myObject, parentObj);
What is the proper way of adding the values to the multidimensional object?
In javascript, dim2Thing[1][1] = ... expressions require dim2Thing[1] to exist. This is why you get the error you mentioned. So you can do two expressions, which should work fine:
dim2Thing[1] = dim2Thing[1] || {}
dim2Thing[1][1] = otherThing
For the last block, you mention that it "overwrites the initial values"
I think what's actually happening here is just that Object.assign is not recursive. It only merges top-level keys. So if parentObj has a key that over-laps with this.myObj, then sub-keys will be lost.
Object.assign({ a: { b: 2} }, { a: { c: 3 } }) // returns { a: { c: 3 } }
This is what I interpret your code as trying to do - though I am unfamiliar with vue.js at this time, so I cannot assure it will have the desired result to your webpage:
let value = "X";
let client = 1;
let index = 1;
const newObj = Object.assign({}, this.myObject);
// if you have lodash _.set is handy
newObj[client] = newObj[client] || {}; // whatever was there, or a new object
newObj[client][index] = value
this.myObject = newObj
Just use an array, thats reactive by design.
If you need to get elements from the array in your template or anywhere just add a find method
// temp
late
<div v-for="(value, idx) in myArray">{{find(obj => obj.id === idx)}}</div>
methods: {
find (searchFunction) {
return this.myArray.find(searchFunction)
}
}
PROBLEM
let x = (0..<10).splitEvery( 3 )
XCTAssertEqual( x, [(0...2),(3...5),(6...8),(9)], "implementation broken" )
COMMENTS
I am running into problems calculating number of elements in the Range, etc...
extension Range
{
func splitEvery( nInEach: Int ) -> [Range]
{
let n = self.endIndex - self.startIndex // ERROR - cannot invoke '-' with an argument list of type (T,T)
}
}
The values in a range are of ForwardIndexType, so you can only advance() them,
or compute the distance(), but the subtraction - is not defined. The advance amount has to be of the corresponding
type T.Distance. So this would be a possible implementation:
extension Range {
func splitEvery(nInEach: T.Distance) -> [Range] {
var result = [Range]() // Start with empty array
var from = self.startIndex
while from != self.endIndex {
// Advance position, but not beyond the end index:
let to = advance(from, nInEach, self.endIndex)
result.append(from ..< to)
// Continue with next interval:
from = to
}
return result
}
}
Example:
println( (0 ..< 10).splitEvery(3) )
// Output: [0..<3, 3..<6, 6..<9, 9..<10]
Note however that 0 ..< 10 is not a list (or array) of integers. To split an array into subarrays you could define a similar extension:
extension Array {
func splitEvery(nInEach: Int) -> [[T]] {
var result = [[T]]()
for from in stride(from: 0, to: self.count, by: nInEach) {
let to = advance(from, nInEach, self.count)
result.append(Array(self[from ..< to]))
}
return result
}
}
Example:
println( [1, 1, 2, 3, 5, 8, 13].splitEvery(3) )
// Output: [[1, 1, 2], [3, 5, 8], [13]]
A more general approach could be to split all sliceable objects. But Sliceable
is protocol and protocols cannot be extended. What you can do instead is to
define a function that takes the sliceable object as the first argument:
func splitEvery<S : Sliceable>(seq : S, nInEach : S.Index.Distance) -> [S.SubSlice] {
var result : [S.SubSlice] = []
var from = seq.startIndex
while from != seq.endIndex {
let to = advance(from, nInEach, seq.endIndex)
result.append(seq[from ..< to])
from = to
}
return result
}
(Note that this function is completely unrelated to the (extension) methods
defined above.)
Example:
println( splitEvery("abcdefg", 2) )
// Output: [ab, cd, ef, g]
println( splitEvery([3.1, 4.1, 5.9, 2.6, 5.3], 2) )
// Output: [[3.1, 4.1], [5.9, 2.6], [5.3]]
Ranges are not sliceable, but you could define a separate function that takes a
range argument:
func splitEvery<T>(range : Range<T>, nInEach : T.Distance) -> [Range<T>] {
var result : [Range<T>] = []
var from = range.startIndex
while from != range.endIndex {
let to = advance(from, nInEach, range.endIndex)
result.append(from ..< to)
from = to
}
return result
}
Example:
println( splitEvery(0 ..< 10, 3) )
// Output: [0..<3, 3..<6, 6..<9, 9..<10]
In Python I can make any class support indexing by overriding __getitem__ like so:
class Test:
def __getitem__(self, key):
return self.data[key]
Does Dart have a similar construct for this?
Assuming that the __getitem__ thing lets you use the "indexing" syntax (object[index]), yes, Dart lets you do the same by defining operator []. Example:
class Test {
var data = {
"a": 1,
"b": 2
};
operator [](index) => data[index];
}
main() {
var t = new Test();
print(t["a"]);
print(t["b"]);
}
You can also define the "opposite" operator []=:
class Test {
Map data = {
"a": 1,
"b": 2
};
operator [](index) => data[index];
operator []=(index, value) { data[index] = value; }
}
main() {
var t = new Test();
print(t["a"]);
print(t["b"]);
t["c"] = 3;
print(t["c"]);
}