When creating an instance of an object, I'm having trouble setting its properties if I assign the property to another variable.
Eg. For an object containing nested objects, I want to assign one of the children to a temporary var to make it easier to work with.
Instead of doing this (which works):
myObj.myChildObject[0].someOtherChild[0].property = "something"
I'm trying to do:
var t = myObj.myChildObject[0].someOtherChild[0]
t.property = "something"
// this doesn't throw an error but doesn't change the value of myObj
What gives?
Edit>
Here's a contrived example to illustrate:
class Car { var name: String = "" }
var merc = Car()
var obj = merc.name
merc.name = "C63 AMG"
obj = "E300"
print("merc.name: \(merc.name)") // prints merc.name: C63 AMG
print("obj.name: \(obj)") // prints obj.name: E300
var ob2 = merc
ob2.name = "Toyota"
print("ob2.name: \(ob2.name)") // prints ob2.name: Toyota
print("merc.name: \(merc.name)") // prints merc.name: Toyota
So assigning the class instance to a var creates a reference. But assigning a property of that object to another var creates a copy?
I read through https://developer.apple.com/swift/blog/?id=10 and still don't get it :(
In the above, 'obj' is not a struct, enum or tuple, so shouldn't it be a reference type?
If myObj.myChildObject[0].someOtherChild[0] is a value type (I.e. a strict, direct enum or tuple), it's being copied upon assignment to t. Subsequent mutations on t only mutate that copy, and the original instance are unchanged.
You would have to reassign t back in:
var t = myObj.myChildObject[0].someOtherChild[0]
t.property = "something"
myObj.myChildObject[0].someOtherChild[0] = t
This is discussed in the Swift language guide.
Related
I am using Node v16.15.1 and TypeScript v4.7.4
I want to split an object into multiple objects, and then insert each object as a value in another object.
i.e.
{key1:"value1", key2:"value2"}
-> {key1:"value1"} and {key2:"value2"}
-> {key3:"value3", key4:"value4", key5:{key1:"value1"}} and {key3:"value3", key4:"value4", key5:{key2:"value2"}}
Below is the code I am using:
let a:any = {}
let entries = Object.entries({key1:"value1", key2:"value2"});
for(const el of entries) {
let b = a;
b.key = Object.fromEntries(new Map([el]));
console.log(b.key);
console.log(b)
}
However, the output I get is this.
{key2:"value2"} is in both objects, instead of just the second one.
If I use the following code, however, I get the correct result:
let entries = Object.entries({key1:"value1", key2:"value2"});
for(const el of entries) {
let b:any = {};
b.key = Object.fromEntries(new Map([el]));
console.log(b.key);
console.log(b)
}
The problem with this is that I am not inserting into a blank object, and am passing it as a parameter in a function.
Why does this happen?
How would I be able to fix this?
TIA
In javascript, when you do let a:any = {}; and then let b = a; you are assigning to b the references of a (not the value). So if you update b, you are actually updating a because both variable are the same.
If you want b to be a copy of a you should do something like : let b = {...a}.
I'm trying to dynamically create node.js variables that point to objects.
I know I can create a variable dynamically in scope using eval:
var vars = ['a','b']
for(var n=0; n<vars.length; n++) {
eval('var '+vars[n]+' = '+n)
}
console.log(b) // prints 1
The above dynamically creates variables and gives them a value of whatever their index is inside the vars list.
But what if I want to set these dynamic variables to an object reference? Something like the following:
var vars = {a: {}, b:{}}
for(var k in vars) {
eval('var '+k) // create the variable dynamically
new Function('value', k+' = value')(vars[k]) // attempt to set the value
}
a.b = 5
console.log(vars.a.b) // undefined : (
I know why the above doesn't work - the function created by new Function can't see the current scope, and so can't set the value. Is there a way to do what I'm trying to do, such that console.log(vars.a.b) would print "5" rather than "undefined"?
UPDATE:
Hmm, I was wrong that new Function can't see or modify variables in the local scope, since this works:
var obj = {}
eval('var x')
new Function('value', 'x = value')(obj)
obj.a = 5
console.log(x.a) // prints 5
So now I'm pretty confused why my loop above doesn't seem to work..
UPDATE 2:
I just realized that my code actually does work in chrome's console. But its not working in node.js...
UPDATE JUST FOR PHIL:
Here's my situation. I'm using Parsimmon to build a parser combinator. This is how that's done:
var L = Parsimmon.createLanguage({
combinator1: function() {
return Parsimmon.string('hi')
},
combinator2: function() {
return L.combinator1.many()
}
})
I'd like to eliminate the need to write L. before every parser combinator I write. I could do this:
var combinator2 = L.combinator2
But that would require me to add an additional line like that for every combinator I write. As you can see, I can't use with since L is created after I'd be able to write with(L) and if I define my functions below then use them in the object, I'm back to duplicating those function names every time i write a new combinator.
So to summarize, I'd like to loop through L and put all the generated parser combinators into a nice clean variable in scope so I can write combinator1 instead of L.combinator1 (etc).
IIUC, whether or not there are better ways to achieve your goal, if you just remove 'var ' on the dynamic Function, it will operate on the outer (global) scope.
Change:
new Function('value', k+' = value')(vars[k])
to:
new Function('value', k+' = value')(vars[k])
So:
var vars = {a: {}, b:{}}
for(var k in vars) {
eval('var '+k) // create the variable dynamically
new Function('value', k+' = value')(vars[k]) // attempt to set the value
}
a.b = 5
console.log(vars.a.b)
You don't want to declare a new variable with local scope inside the function, you want to operate on the outer scope.
Update to address new question
Your first loop does work. Try introspecting on a or b; they are as should be expected, 0 and 1, respectively.
Update 2 based on info this is for Node.js
Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function , Function always works on the global scope. In Node, this is on global and var variables are not the global scope but are the module's scope. To fix this for Node, you can do the following and omit your eval var declaration (which was overriding for the module the global scope which you have access to inside Function):
var vars = {a: {}, b:{}}
for(var k in vars) {
new Function('value', k +' = value')(vars[k]) // attempt to set the value
}
a.b = 5
console.log(vars.a.b) // 5
In other words, the inner function sets variables which automatically become accessible via global, so your module code, in the absence of any module-scoped var declarations of a overwriting the global, you can set the global a properties as with the line a.b = 5.
Update 3
Since I had just been addressing your issue in understanding Function, I gave the above information. As per your follow-up comment (and again, without speaking to its suitability for your specific use case), you can operate on the object via eval as follows:
var vars = {a: {}, b:{}}
for(var k in vars) {
eval('var '+k+' = vars["'+k+'"]')
}
a.b = 5
console.log(vars.a.b) // 5
But to reiterate the warnings made by others--using eval is generally not a good idea and can be dangerous when the variables referenced include arbitrary user data...
change this line :
from :
a.b = 5
to
vars.a.b = 5
because a is undefined in your case that's why it is now allowing to create a new property
vars.a will return you a object not only a as a is vars property.
Using playground to simulate a problem with a core data based app turned up an issue I can't seem to understand.
class Pole {
var length: NSNumber!
}
var poles: [Pole] = []
let pole1 = Pole()
pole1.length = 1
poles.append(pole1)
let pole2 = Pole()
pole2.length = 2
poles.append(pole2)
var sum = poles.reduce(0) { $0 + $1.length } // error Could not find member 'length'
The property (attribute) named length is NSNumber as it is in a NSManagedObject class (entity).
Changing the Type from NSNumber! to Int! allows the line to compile and run correctly in playground.
Leaving the the Type as NSNumber! and changing the the offending line as follows:
var sum = poles.reduce(0) { $0 + Int($1.length) }
also compiles and run correctly in playground. The next step, taking this to the app, using actual NSManagedObject entity and attribute compiles but fails at runtime. The failure is 'unwrapping a nil'.
So bottom line I can't figure out how to use the reduce function when the attribute is a NSNumber and casting it to an Int doesn't seem to be acceptable.
NSNumber is a Foundation class that is used to wrap numbers that would otherwise be represented in a value type, so they can be used wherever a reference type is expected. Swift bridges integer literals to NSNumber, but not the other way around, so the original reduce closure was trying to add an integer literal to an NSNumber, getting confused, and giving you a weird error message.
Creating a new Int is one way to handle the problem:
var sum = poles.reduce(0) { $0 + Int($1.length) }
Or you can use NSNumber's integerValue property, which returns the instance's value as an Int:
var sum = poles.reduce(0) { $0 + $1.length.integerValue }
HashSet Object ct_set City Object
how can i initialize a ProrityQueue object ct_pq with elements in ct_set with order from my populatation comparator
Create the PriorityQueue with your Comparator, and then just call addAll:
HashSet<City> cities = ...;
PriorityQueue<City> queue = new PriorityQueue(new CityComparator());
queue.addAll(cities);
Note that if you've really got a HashSet<Object> instead (your question is far from clear) you should probably try to change your code so that you do have a HashSet<City> instead. Or you can always just cast each element:
HashSet<Object> cities = ...;
PriorityQueue<City> queue = new PriorityQueue(new CityComparator());
for (Object x : cities) {
queue.add((City) x);
}
The "C# 4.0 IN A NUTSHELL" 4th edition book by the Albaharis states on page 249:
". . . calling object.ReferenceEquals guarantees normal referential equality."
So, I decided to test this out.
First I tried value types like this.
int aa1 = 5;
int aa2 = aa1;
MessageBox.Show("object.ReferenceEquals(aa1,aa2) is: " + object.ReferenceEquals(aa1, aa2));
And just as I expected, the result was false:
object.ReferenceEquals(aa1, aa2) is: False
Life was good.
Then I tried a mutable reference type like this.
System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;
MessageBox.Show("object.ReferenceEquals(sblr1,sblr2) is: " + object.ReferenceEquals(sblr1, sblr2));
And just as I expected, the result was true
object.ReferenceEquals(sblr1, sblr2) is: True
Life was still good.
Then I figured that since it is a mutable reference type, then if I change one variable to null, then both should be null.
So I tried the following.
System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;
sblr1 = null;
MessageBox.Show("object.ReferenceEquals(sblr1,sblr2) is: " + object.ReferenceEquals(sblr1, sblr2));
And I expected them to both be null.
But the result I got was False:
object.ReferenceEquals(sblr1, sblr2) is: False
Now life was not so good.
I thought that if it overrode the memory location of sblr1, then it would be overriding the memory location of sblr2 also.
Then I thought that maybe they were pointing to two different nulls, so I tried this:
System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
System.Text.StringBuilder sblr2 = sblr1;
sblr2 = null;
MessageBox.Show("sblr1 == " + sblr1 + " and sblr2 == " + sblr2);
But here, only one was pointing to a null like this.
sblr1 == aaa and sblr2 ==
Only one was null.
It was displaying the behavior I'd expect from an immutable reference type like a string object.
With a string object, I can do something like this:
string aa1 = "aaX";
string aa2 = "aaX";
MessageBox.Show("object.ReferenceEquals(aa1,aa2) is: " + object.ReferenceEquals(aa1, aa2));
And they will both reference the same thing like this.
object.ReferenceEquals(aa1, aa2) is: True
because "aaX" only gets written to the assembly once.
But if I do this:
string aa1 = "aaX";
string aa2 = "aaX";
aa1 = null;
MessageBox.Show("After aa1 is null(" + aa1 + "), then aa2 is: " + aa2);
Then they point to different things like this:
After aa1 is null (), then aa2 is: aaX
That's because string objects are immutable. The memory location doesn't get overriden. Rather, the variable points to a different location in Heap memory where the new value exists.
Changing aa1 to null in the above example means that aa1 will point to a different location on the Heap memory.
Why then is the mutable reference type behaving just the same as the immutable reference type?
Edit 4:03PM and 4:08
I've recently tried this:
System.Text.StringBuilder sblr1 = new System.Text.StringBuilder();
sblr1.Append("aaa");
// sblr1 and sblr2 should now both point to the same location on the Heap that has "aaa".
System.Text.StringBuilder sblr2 = sblr1;
System.Text.StringBuilder sblr3 = new System.Text.StringBuilder();
sblr3.Append("bbb");
sblr1 = sblr3;
MessageBox.Show("sblr1 == " + sblr1 + " and sblr2 == " + sblr2 + " and sblr3 == " + sblr3);
Which gave me:
sblr1 == bbb and sblr2 == aaa and sblr3 == bbb
That's more like the result I was expecting.
I see now, thanks to the comments, that I abscent mindedly expected null to act like a memory location.
I thought that if it overrode the memory location of sblr1, then it would be overriding the memory location of sblr2 also.
This is your misunderstanding.
When you write this:
System.Text.StringBuilder sblr2 = sblr1;
You're assigning the sblr2 variable to be a reference to the same instance of StringBuilder as the one pointed to by sblr1. The two variables now point to the same reference.
You then write:
sblr1 = null;
This changes the sblr1 variable to now be a null reference. You didn't change the instance in memory at all.
This has nothing to do with whether the reference is a mutable type or not. You're changing the variables, not the instance which they are referencing.
As for your string example:
That's because string objects are immutable. The memory location doesn't get overridden
This actually is not true. The fact that you're setting one string variable to null doesn't really have anything to do with the string being immutable. That's a separate concern.
Why then is the mutable reference type behaving just the same as the immutable reference type?
The behavior you're seeing has nothing to do with mutability. It is the standard behavior for all reference types (whether immutable or mutable). Mutability is a different issue.
The main issue with mutability is this:
Suppose you have a class, like so:
class Foo
{
public int Bar { get; set; }
}
If you write this:
Foo a = new Foo();
a.Bar = 42;
Foo b = a;
b.Bar = 54;
Console.WriteLine(a.Bar); // Will print 54, since you've changed the same mutable object
With immutable types, this can't happen, since you can't change Bar - instead, if you make an immutable class:
class Baz
{
public Baz(int bar) { this.Bar = bar; }
public int Bar { get; private set; }
}
You would need to write:
Baz a = new Baz(42);
Baz b = a;
// This isn't legal now:
// b.Bar = 54;
// So you'd write:
b = new Baz(54); // Creates a new reference
Alternatively, you could make the class return a new reference on a "change" operation, ie:
class Baz
{
public Baz(int bar) { this.Bar = bar; }
public int Bar { get; private set; }
public Baz Alter(int newValue) { return new Baz(newValue); } // May copy other data from "this"
}
Then when you'd write:
Baz a = new Baz(42);
Baz b = a.Alter(54); // b is now a new instance
This is what happens with string - all of the methods return a new instance, since a string is immutable, so you can never "change" the existing copy.
This has nothing to do with mutability. The rules involved here are the same for all reference types. A non-ref non-out variable (or member, or slot in a collection) of a reference type is a reference (duh). That means it refers to some object. It does not refer to another reference, or to a location where another reference is (e.g. to a variable). When you assign to a variable (or member, or slot in a collection), you change what reference is in that place; you do not overwrite any part of any object (except, of course, the member you assign to, if it's a member).
In your code, there are two variables srbl1 and srbl2, each of which stores a reference to the same string builder object. Assigning to either changes overwrites one of those reference (e.g. with null, or with a reference to a different object).
Changing a reference is just changing what something refers to. It doesn't change the object itself.
One way to look at it is to imagine an array of integers:
int[] foo = new int[] {0, 1, 2, 3, 4, 5};
You can create two indexes that refer to items in the array:
int ix = 1;
int iy = ix;
And then foo[ix] == foo[iy].
If you then write foo[ix] = 42, then foo[ix] == foo[iy] is still true because you changed the value that the indexes referred to.
But if you change the index so that ix = 3, then ix and iy are referring to different things.
Reference types work exactly the same way.
When you write sblr1 = new StringBuilder(), you've created a new StringBuilder object instance and made sblr1 point to it. If you then write sblr2 = sblr1, you're just making sblr2 point to the same thing. And then sblr1 = null just says, "sblr1 isn't pointing to anything anymore." It doesn't actually affect the item it refers to, just as changing the index to an array doesn't affect the value of the item being indexed.