Get the value of an instance variable given its name
The most idiomatic way to achieve this is:
some_object.instance_variable_get("@#{name}")
There is no need to use +
or intern
; Ruby will handle this just fine. However, if you find yourself reaching into another object and pulling out its ivar, there's a reasonably good chance that you have broken encapsulation.
If you explicitly want to access an ivar, the right thing to do is to make it an accessor. Consider the following:
class Computer
def new(cpus)
@cpus = cpus
end
end
In this case, if you did Computer.new
, you would be forced to use instance_variable_get
to get at @cpus
. But if you're doing this, you probably mean for @cpus
to be public. What you should do is:
class Computer
attr_reader :cpus
end
Now you can do Computer.new(4).cpus
.
Note that you can reopen any existing class and make a private ivar into a reader. Since an accessor is just a method, you can do Computer.new(4).send(var_that_evaluates_to_cpus)
Get instance variable by its name
This is more or less what hashmaps were made for, for pairing a key (in this case a string) to an object
class B
{
private HashMap<String,A> theAs=new HashMap<String,A>(); //give better name
public B ()
{
theAs.put("a1",new A ("a1")); //A probably doesn't need to keep its own name internally now, but have left it as its in your original code
theAs.put("a2",new A ("a2"));
theAs.put("a3",new A ("a3"));
}
public A get_A_byName (String name)
{
return theAs.get(name);
}
}
Notes
In Java 7+ you don't need to add the second
<String,A>
, diamond
inference will do, so in Java 7 or higher it would be:private HashMap<String,A> theAs=new HashMap<>();
How to access instance variable from its string name ?
Use instance_variable_set
and instance_variable_get
. Keep in mind the string needs to have the leading @
:
@foo = "bar"
# => "bar"
instance_variable_get("@foo")
# => "bar"
instance_variable_set("@foo", "baz")
# => "baz"
@foo
# => "baz"
Access instance variable by using instance name
If by "call" you actually mean call and not just "access" you can simply implement a __call__
method:
class PropertyExpr:
def __init__(self, value, expr):
self.value = value
self.expr = expr
def __call__(self):
return self.value
prop1 = PropertyExpr(5, "test")
val = prop1()
print(val)
Output:
5
In that case, the result of calling prop1()
can be really anything.
Other than that, what you want is not possible. You could override the __new__
method, but that will also change the type of what you're creating. So if you're returning 5
your object will be 5
, but it will also be an int
and no longer an instance of PropertyExpr
and all your additional attributes will be lost:
class PropertyExpr():
def __new__(cls, value, expr):
return value
def __init__(self, value, expr):
self.value = value
self.expr = expr
prop1 = PropertyExpr(5, "test")
print(prop1, type(prop1))
try:
print(prop1.expr)
except Exception as e:
print(e)
Output:
5 <class 'int'>
'int' object has no attribute 'expr'
After some trying around, I've figured out a way to dynamically change the type of the constructor, however I would advise against actually using this:
class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("FakeType", (tp,), kwargs)(value)
prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr)
prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr)
prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr)
Output:
5 -> expr int
['5'] -> expr list
abc -> expr string
You can pass the type you wish to sub-class as the first parameter, the second parameter should be a value accepted by the type's contructor and then you can pass in arbitrary kwargs that will be added as attributes to the created object.
is there a way to make is so that type(prop1) would still be PropertyExpr? Edit: for example, if we can do isinstance(prop1, PropertyExpr) = True then everything would be perfect
I could not get that to work (that does not mean that others cannot), but I managed to make it work with multi inheritance, so you can use isinstance(prop1, PropertyExprMixin)
:
class PropertyExprMixin:
pass
class PropertyExpr:
def __new__(cls, tp, value, *args, **kwargs):
return type("PropertyExprMixin", (tp,PropertyExprMixin), kwargs)(value)
prop1 = PropertyExpr(int, 5, expr="expr int")
print(prop1, " -> ", prop1.expr, type(prop1), isinstance(prop1, int), isinstance(prop1, PropertyExprMixin))
prop2 = PropertyExpr(list, "5", expr="expr list")
print(prop2, " -> ", prop2.expr, type(prop2), isinstance(prop2, list), isinstance(prop2, PropertyExprMixin))
prop3 = PropertyExpr(str, "abc", expr="expr string")
print(prop3, " -> ", prop3.expr, type(prop3), isinstance(prop3, str), isinstance(prop3, PropertyExprMixin))
Output:
5 -> expr int <class '__main__.PropertyExprMixin'> True True
['5'] -> expr list <class '__main__.PropertyExprMixin'> True True
abc -> expr string <class '__main__.PropertyExprMixin'> True True
Retrieve specific values of instance variable from each object of a list containing Objects of different classes
Use Reflection
Assuming the classes do not share a common base class or interface which contains the method you may use Reflection to invoke the method. There is a lot of error handling to make it work properly but the following example waves that away and forces the caller to deal with it.
public String getName(Object named)
throws NoSuchMethodException,
SecurityException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method = named.getClass().getMethod("getName");
return (String) method.invoke(named);
}
The following iterates a List of objects and invokes the above method. Since the Exceptions are percolating up, this method has to handle them. You could just catch Exception here but I left the full list for completeness.
public void printNames(List<Object> namedObjects) {
for (Object obj : namedObjects) {
try {
System.out.println(getName(obj));
} catch (NoSuchMethodException |
SecurityException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException e) {
System.err.println("Failed to invoke getName on object - skipping");
}
}
}
Getting the value of a variable based on a string name in Java
You can use reflection and a helper method for this:
public static Object getFieldValue(Object target, String fieldName){
// get field from class
Field field = target.getClass().getDeclaredField(fieldName);
// set accessability to "public"
field.setAccessible(true);
try{
// get value
return field.get(target);
} catch(IllegalAccessException e){
// or throw error
throw new IllegalStateException(e);
}
}
This can then be invoked easily with:
Object value = getFieldValue(this, "sample");
The snippet uses Class#getDeclaredField(String)
as suggested by @aszro in the comments. This method returns the field by name or throws a NoSuchElementException
if no field exists.
Access instance variable using its name (as symbol)
Here are ways to do both techniques. Assuming we already have your class definition,
position = Position.new(1, 2)
axis = :x
position.send axis #=> 1
axis = :y
position.send axis #=> 2
The Object#send
method accepts at least a symbol representing the name of the method to call, and call it. You can also pass arguments to the method after the name and a block, too.
The second way to do this (using your Position#get
method) is
class Position
def get(axis)
send axis
end
end
position = Position.new(1, 2)
axis = :x
position.get axis #=> 1
axis = :y
position.get axis #=> 2
I recommend this way because it encapsulates the technique for getting the values. Should you need to change it later, you don't need to change all the code that uses Position
.
Get instance variable by name in Swift
From the Swift docs (emphasis mine):
If you have experience with Objective-C, you may know that it provides two ways to store values and references as part of a class instance. In addition to properties, you can use instance variables as a backing store for the values stored in a property.
Swift unifies these concepts into a single property declaration. A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement. All information about the property—including its name, type, and memory management characteristics—is defined in a single location as part of the type’s definition.
Depending on how pointerOfIvarForPropertyNamed
is used, you may not be able to literally translate that method into Swift, since it doesn't have Ivars. You may be able to use Key-Value Coding to achieve what you want.
If you want to expand on how this method is used, I can expand my answer appropriately.
Related Topics
What to Use Instead of 'Render :Text' (And 'Render Nothing: True') in Rails 5.1 and Later
Rails Route to Username Instead of Id
Get Single Char from Console Immediately
Portable Ruby on Rails Environment
Rvm Is Not a Function, Selecting Rubies with 'Rvm Use ...' Will Not Work
Installing Cocoapods: No Response
How to Find the Key of the Largest Value Hash
What Does 'Def Self.Function' Name Mean
Where to Put Ruby Helper Methods for Rails Controllers
Creating a Model That Has a Tree Structure
Is There an Expect Equivalent Gem for Ruby