Case insensitive NSPredicate for strings from an array? - cocoa

I have a situation where I want to fetch objects from my core data store by the username key, but I want the comparison to be case-insensitive. The predicate I have is this:
username IN $usernames
I then do a variable substitution with an array of strings that are the usernames I want to find. It works but is case-sensitive. I'd like to do something like this, I think:
username IN[c] $usernames
Unfortunately that doesn't appear to work. The string comparison must still be happening in a case-sensitive way. (I don't get an error about it being an unsupported query.)
Is there a different way to write this predicate so it works the way I need or am I just missing something obvious here?

The case modifier on the IN operator is apparently ignored when executing a fetch against the SQLite store. (You omitted the store type from your question.)
I'd recommend filing a bug against the documentation so that this limitation/behavior can be documented.
I'd also recommend filing a feature request in the bug reporter so that this can be considered for future support.
In the meantime, you'll have to pull your fetch request out of the data model and build it up programatically. You can build a compound predicate OR predicate that does a case insensitive equality match for each of your values (and test that it meets your performance needs.)
Note that if you are supporting OS targets prior to 10.6 the case modifier on == is not supported, in which case yet another alternate solution will be required.

You might try something like ANY $usernames LIKE[c] username. I've done something similar, where instead of the variable substution, I just have a key path like "persons.name", and that predicate works for me. Not sure if it works any differently with a variable there instead of a key path, but it's worth a shot.

Here is another workaround, but requires you to change the MOM.
Make username a full blown entity. Create the inverse relationship between username and whatever your other entity is.
Now for the fetch request, set the entity as "Username" then run this predicate (assuming a "name" property and "parent" property):
[NSPredicate predicateWithFormat:"(name like[c] %#) && (parent == %#)", theUserName, theParentObject]
This may be overkill, but will allow you to run your search as desired.

Although this question is several years old I just stumbled upon the same problem and solved it like this:
NSArray *values = #[#"FOO", #"bar" ,#"lorem"];
NSString *predicate;
predicate = #"value LIKE[cd] '";
predicate = [predicate stringByAppendingString:
[values componentsJoinedByString:
#"' OR value LIKE[cd] '"]];
predicate = [predicate stringByAppendingString:#"'"];
NSLog(#"%#", predicate);
// Output:
// value LIKE[cd] 'FOO' OR value LIKE[cd] 'bar' OR value LIKE[cd] 'lorem'
This creates a static predicate expression from the value list. Maybe it will be usefull to someone.
UPDATE 2:
Actually it seems like the updated solution below does not work with sqlite and produces an
'NSInvalidArgumentException',
reason: 'unimplemented SQL generation for predicate : ... (bad LHS)'
I think for array/dict/set comparisions and IN there are only keys allowed on the left side of the operator.
UPDATE:
After some research on expressions and predicates I found another aproach, that seems to work quite well:
NSPredicate *predicate =
[NSPredicate predicateWithFormat:
#"lowercase(value) IN %#", values];
This uses the function expression lowercase: to get the lowercase representation of the value. All that's left to do is to ensure that all entries of values are lowercase and you'll be able to use case insensitive IN expressions.

Related

Azure ADF expression that returns either an existing array, or an empty array, based on a bool

I have a boolean expression: equals(myStringValue, targetStringValue)
I have an array expression which might or might not be valid, depending on the boolean condition myArrayExpression.
I want to write:
if(
equals(myStringValue, targetStringValue),
myArrayExpression,
?????
)
where ????? is an expression that returns an empty array.
Naturally, this is an XY-problem.
I definitely want to know how to do this directly, because understanding how this language works well is important to me. But if you want to know about the XY problem, it's over here: Azure ADF GetMetadata childItems if folder might not exist
Defining an array variable, with no default value, and then referencing that does work.
But seems very sad - now we've got an extra variable floating around for no reason :(
You can use if (x, Y, skip(createArray(''), 1))

How to express empty hash

Is there any way to express the empty hash in Sorbet?
I have the attribute that has a well defined shape or is set to {}. Obviously T.type_alias({}) won't work because {} works as the Hash which is translated to T::Hash[T.untyped, T.untyped]. The easiest solution would be to have something like T.nil and then use it as T::Hash[String, T.nil] (fetching the value for the non-existing key will always return a nil value), but there is no such construction.
The workaround is to change the code to not accept the empty hash there and handle it differently. Then, it is possible to have T.nilable(SomeType). However, I would like to keep the code as it is now and just add a proper type signature.
I think you could use T::Hash[String, T.nilable(String)]? However when I try on sorbet.run, it seems to indicate that it won't type-check arguments of the method correctly.
I have the attribute that has a well defined shape
You may want to use T::Struct to represent this. Sorbet doesn't have great support for shape yet.
T.nil
You can use NilClass. It's actually a Ruby class.

setStringValue and pointers

I have a unique situation with setStringValue: and hoping someone could clear this up:
Using the following theoretical example (not literal) code:
NSString *myVar;
[myOutlet setStringValue:myVar];
It appears that for any string value such as:
myVar = #"hello";
a pointer is passed to myOutlet and the NSTextField points to the same memory location as myVar, essentially making them identical. In essence:
myVar == [myOutlet stringValue];
returns TRUE.
HOWEVER
in this situation:
myVar = #"";
it seems as if it is not passing a pointer, but rather NSTextField is creating it's own independent memory location to store it's empty string, essentially:
myVar == [myOutlet stringValue];
return FALSE.
Can anyone confirm whether this is true, and if so, explain why? I believe this to be the source of a very complex problem I'm having in a piece of code I'm working on and I'm trying to wrap my mind around the root of the problem.
Thanks!
Basically, it's pure chance that the first situation works out. These pointers are absolutely not guaranteed to be equal, and if you need to compare strings, use -isEqualToString: always.
What you're running into is probably an optimization of some sort, to avoid storing #"hello" more than once. We have no way of knowing when this will or will not happen, and it may change in the future, or from device to device.

Sort NSFetchRequest by a predicate

I have a SQLite-backed core data storage and would like to fetch a list of managed objects using NSFetchRequest. I want said list to be sorted by a boolean value that can be easily calculated at the database level. I know this because it’s possible to formulate the same conditions using an NSPredicate, which would look as follows:
[NSPredicate predicateWithFormat:#"uid = %#", currentUID]
Sadly, there seems to be no way to formulate a condition like this using an NSSortDescriptor. How do I best go about this? Do I fetch two lists, one with
[NSPredicate predicateWithFormat:#"uid = %#", currentUID]
and one with
[NSPredicate predicateWithFormat:#"uid != %#", currentUID]
and combine them later on? Can I then still elegantly use a NSFetchedResultsController?
Or should I fetch all items and sort them later in code? Or is there anything I’ve missed.
Just create an array (even if it contains only a single element) of NSSortDescriptors to sort the result as desired.
You use setSortDescriptors: to set them.
The fetch can handle both predicates and sorting at the same time.
What you're asking for doesn't make sense.
A predicate is used to select which objects should be returned in a set of results. This is determined by evaluating the predicate against each object to see if the predicate evaluates to YES or NO (a binary value, or one of 2 possible values). If it evaluates to YES, then the object is included. If it evaluates to NO, then the object is excluded. There is no middle ground.
By contrast, a sort descriptor evaluates to less than, equal, or greater than. In other words, it is a ternary value (it can be one of 3 things). There is no way to express a ternary value with a predicate (since a predicate is binary), and so using a predicate as a sort descriptor makes absolutely no sense. The API doesn't allow it, because logic doesn't allow it.
So the real question is: what are you trying to do? Why do you think you need a predicate in your sort descriptors?

Tips on understanding Ruby syntax, when to use ?, and unless

Is the keyword unless the same as if?
When do you use ??
I've seen:
if someobject?
I know it checks against nil correct?
Is the keyword 'unless' the same as 'if' ?
No, it's the opposite.
unless foo is the same as if !foo
if someobject?
I know it checks against nil correct?
No it calls a method named someobject?. I.e. the ? is just part of the method name.
? can be used in methodnames, but only as the last character. Conventionally it is used to name methods which return a boolean value (i.e. either true or false).
? can also be used as part of the conditional operator condition ? then_part : else_part, but that's not how it is used in your example.
unless is actually the opposite of if. unless condition is equivalent to if !condition.
Which one you use depends on what feels more natural to the intention you're expressing in code.
e.g.
unless file_exists?
# create file
end
vs.
if !file_exists?
# create file
end
Regarding ?, there is a convention for boolean methods in Ruby to end with a ?.
This statement:
unless conditional expression
Is the equivalent to:
if not (conditional expression)
In Ruby you can end your method names with a question mark which is normally used to show that it is a boolean method.
With Rails a check against nil would look like this:
someobject.nil?
This calls the nil?() method of the object, which returns true for NilObject and false for anything else.
I think the convention for ?-suffix is to use it when naming a method that returns a boolean value. It is not a special character, but is used to make the name of the method easier to understand, or at least I think that's what the intention was. It's to make it clear that the method is like asking a question: it shouldn't change anything, only return some kind of status...
There's also !-suffix that I think by convention means that the method may have side-effects or may modify the object it is called on (rather than return a modified copy). Either way, the ! is to make you think carefully about calling such a method and to make sure you understand what that method does.
I don't think anything enforces these conventions (I've never tried to break them) so of course you could abuse them horribly, but your fellow developers would not be happy working with your code.
for unless see here: http://railstips.org/blog/archives/2008/12/01/unless-the-abused-ruby-conditional/
if someobject?
The appending of a '?' here only means that it returns a boolean.

Resources