Is it possible to pass an array to a SELECT … WHERE … IN statement via FMDB?
I tried to implode the array like this:
NSArray *mergeIds; // An array with NSNumber Objects
NSString *mergeIdString = [mergeIds componentsJoinedByString:#","];
NSString *query = #"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *result = [database executeQuery:query, mergeIdString];
This only works if there is exactly 1 object in the array, which leads me to believe that FMDB adds quotes around the whole imploded string.
So I tried passing the array as is to FMDB's method:
NSArray *mergeIds; // An array with NSNumber Objects
NSString *query = #"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *result = [database executeQuery:query, mergeIds];
Which doesn't work at all.
I didn't find anything about it in the README or the samples on FMDB's github page.
Thanks, Stefan
I was having the same issue, and I figured it out, at least for my own app. First, structure your query like so, matching the number of question marks as the amount of data in the array:
NSString *getDataSql = #"SELECT * FROM data WHERE dataID IN (?, ?, ?)";
Then use the executeQuery:withArgumentsInArray call:
FMResultSet *results = [database executeQuery:getDataSql withArgumentsInArray:dataIDs];
In my case, I had an array of NSString objects inside the NSArray named dataIDs. I tried all sorts of things to get this SQL query working, and finally with this combination of sql / function call, I was able to get proper results.
Well I guess I have to use executeQueryWithFormat (which, according to FMDB documentation is not the recommended way). Anyway, here's my solution:
NSArray *mergeIds; // An array of NSNumber Objects
NSString *mergeIdString = [mergeIds componentsJoinedByString:#","];
NSString *query = #"SELECT * FROM items WHERE last_merge_id IN (?)";
FMResultSet *res = [self.database executeQueryWithFormat:query, mergeIdString];
Adding onto Wayne Liu, if you know that the strings do not contain single or double quotes, you could simply do:
NSString * delimitedString = [strArray componentsJoinedByString:#"','"];
NSString * sql = [NSString stringWithFormat:#"SELECT * FROM tableName WHERE fieldName IN ('%#')", delimitedString];
If the keys are strings, I use the following code to generate the SQL command:
(assume strArray is an NSArray containing NSString elements)
NSString * strComma = [strArray componentsJoinedByString:#"\", \""];
NSString * sql = [NSString stringWithFormat:#"SELECT * FROM tableName WHERE fieldName IN (\"%#\")", strComma];
Please note: if any elements in strArray could potentially contain the "double quote" symbols, you need to write extra codes (before these 2 lines) to escape them by writing 2 double quotes instead.
I created a simple FMDB extension to solve the problem:
FMDB+InOperator on GitHub
Here's a Swift extension for FMDatabase that breaks array query parameters into multiple named parameters.
extension FMDatabase {
func executeQuery(query: String, params:[String: AnyObject]) -> FMResultSet? {
var q = query
var d = [String: AnyObject]()
for (key, val) in params {
if let arr = val as? [AnyObject] {
var r = [String]()
for var i = 0; i < arr.count; i++ {
let keyWithIndex = "\(key)_\(i)"
r.append(":\(keyWithIndex)")
d[keyWithIndex] = arr[i]
}
let replacement = ",".join(r)
q = q.stringByReplacingOccurrencesOfString(":\(key)", withString: "(\(replacement))", options: NSStringCompareOptions.LiteralSearch, range: nil)
}
else {
d[key] = val
}
}
return executeQuery(q, withParameterDictionary: d)
}
}
Example:
let sql = "SELECT * FROM things WHERE id IN :thing_ids"
let rs = db.executQuery(sql, params: ["thing_ids": [1, 2, 3]])
Related
I am getting data from my database and the data is being retrieved using a while loop.
success = [db executeQuery:#"SELECT * FROM apidataTwo;"];
while([success next]){
int first = [success intForColumn:#"id"];
NSString *id = [NSString stringWithFormat:#"%d",first];
[_tempArray addObject:id];
NSString *country_name = [success stringForColumn:#"country_name"];
[_tempArray addObject:country_name];
NSString *breezometer_description = [success stringForColumn:#"breezometer_description"];
[_tempArray addObject:breezometer_description];
NSString *country_description = [success stringForColumn:#"country_description"];
[_tempArray addObject:country_description];
NSString *dateString= [success stringForColumn:#"dateString"];
[_dateSectionArray addObject:dateString];
[_dataDictionary setObject:_tempArray forKey:dateString];
}
Suppose we get the same key in different iterations of the loop. When I pass the array to the NSMutableDictionary, the previous values will be replaced and lost.
And if I keep updating the NSMutableArray, then the values of a previous key will also be added to a different key.
So in situations like this when we want to concatenate the values to the same key, then what should be our approach.
The dictionary should look like this:
{
2016-10-05" = (
5,
"United States",
"Fair Air Quality",
"Good air quality"
);
"2016-10-06" = (
5,
"United States",
"Fair Air Quality",
"Good air quality"
);
}
Once you have figured out the key for this batch of data, try to retrieve an object from the dictionary for that key. If objectForKey: returns nil, then create a new mutable array. Then set that array as the dictionary's object for that key.
Every new batch of data is then added to the array, not to the dictionary. Here's a sketch of the structure:
while( /* processing data */){
// Collect this batch
NSArray * entry = ...;
// Figure out the dictionary key for the batch.
// (it doesn't have to be a string, this is just for example)
NSString * key = ...;
// Try to retrieve the object for that key
NSMutableArray * entries = _dataDictionary[key];
// If the result is `nil`, the key is not in the dictionary yet.
if( !entries ){
// Create a new mutable array
entries = [NSMutableArray array];
// Add that to the dictionary as the value for the given key
_dataDictionary[key] = entries;
}
// Now `entries` is a valid `NSMutableArray`, whether it already
// existed or was just created. Add this batch.
[entries addObject:entry];
// Move on to the next batch.
}
This is part of an incoming array:
variantArray: (
(
{
CardinalDirection = "North-West";
DirectionVariantId = "DcCi_1445_171_0_0";
Distance = "2.516606318971459";
RouteName = "Woodsy";
Shape = {
Points = (
{
I want to get the value of DirectionVariantId
I would normally loop and use
NSMutableArray *myString = [variantArray[i] valueForKey:#"DirectionVariantId"];
This isn't working and results in an exception when I try to examine the last character in the string:
NSString *lastChar = [myString substringFromIndex:[myString length] - 1];
This is a new data set for me and I'm missing something..
Thanks for any tips.
Json contain two curly bracket means nested array.
Try:
NSString *myString=[[[variantArray objectAtIndex:0] objectAtIndex:0] objectForKey:#"DirectionVariantId"];
I think you're looking for [variantArray[i] objectForKey:#"DirectionVariantId"];
You'd need to convert the object within your incoming array (variantArray[i]) to a NSDictionary but it might already be judging by your original output.
I'm having trouble writing the following statement into my Database Controller class within an IOS project.
SELECT * FROM GPSJob WHERE sourceMonitor = '%#' AND positionNumber = %d;
I understand the need to use mysqlite3_open(), mysqlite3_prepare_v2() and mysqlite_step() but I cannot for the life of my convert the values given from step into an NSString and a NSNumber!
You will need something like this:
NSString *querySQL = #"SELECT * FROM GPSJob WHERE sourceMonitor = ? AND positionNumber = ?";
Then use the following for String
sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
and following for int
sqlite3_bind_int(sqlite3_stmt*, int, int);
I am implementing csv splitting in my project . can I know how to implement CSV splitting in ios. say for example i have a string #"one,\"two,three\",four" . I need output as below in an array format which containts 3 element
one
two,three
four
You can use this code but, it is not proper way:
NSString *StrDataOfCSV=#"one,\"two,three\",four";
NSMutableArray * csvArray= [[NSMutableArray alloc]init];
NSArray * firstSeparated=[StrDataOfCSV componentsSeparatedByString: #"\","];
for (NSString * strCom in firstSeparated) {
NSArray * arrCom=[strCom componentsSeparatedByString:#",\""];
[csvArray addObjectsFromArray:arrCom];
}
Something like this to "normalize" your input string, replacing the commas with a new separator character, one that you can guarantee won't appear in your input (e.g. a |), and removing the quotes from the quoted fields:
char *cbCSVRecord = "one,\"two,three\",four";
bool bQuotedField = false;
// normalize
for (int i = 0,j = 0;i < (int)strlen(cbCSVRecord);i++)
{
if ((cbCSVRecord[i] == '\n') || (cbCSVRecord[i] == '\r'))
cbCSVRecord[i] = '\0';
// Not a double quote?
if (cbCSVRecord[i] != '"')
{
// if a comma NOT within a quoted field, replace with a new separator
if ((cbCSVRecord[i] == ',') && (!bQuotedField))
cbCSVRecord[i] = '|';
// new output
cbCSVRecord[j] = cbCSVRecord[i];
j++;
}
// Is a double quote, toggle bQuotedField
else
{
if (!bQuotedField)
bQuotedField = true;
else
bQuotedField = false;
}
}
cbCSVRecord[j] = '\0';
please note that I roughed out this code, and it is plain C code. It should either work or have you pretty close.
An input of #"one,\"two,three\",four" should become #"one|two,three|four", and you can then use:
NSArray *listItems = [list componentsSeparatedByString:#"|"];
Use NSString's componentsSeparatedByString:
NSString *list = #"Karin, Carrie, David";
NSArray *listItems = [list componentsSeparatedByString:#", "];
I have the json value like this ,
{
"product_color" = Black;
"product_description" = "The new Macbook air is ultraslim";
"product_id" = 1;
"product_large_image_url" = "https://www.gstatic.com/webp/gallery3/3_webp_ll.png";
"product_name" = "MacBook Air";
"product_price" = "$2500";
"product_size" = Small;
"product_stocks" = 50;
"product_thumb_image_url" = (
"https://www.gstatic.com/webp/gallery3/2_webp_ll.png",
......
......
);
}
and I want to insert the product_thumb_image_url array in a single attribute through core data,
what i have tried is:
+(void)insertingProduct: (int16_t) cId :(NSDictionary *)dictionary{
DataModel *dModel = [self dataModel];
Products *productDetails=[dModel createEntity:products];
productDetails.product_id=[[dictionary valueForKey:productid] integerValue];
productDetails.product_large_image_url = [dictionary valueForKey:productlargeImage];
productDetails.product_name=[dictionary valueForKey:productname];
productDetails.product_price=[[dictionary valueForKey:productPrice] integerValue];
productDetails.product_sizes=[dictionary valueForKey:productsizes];
productDetails.product_stocks=[[dictionary valueForKey:productstocks] integerValue];
productDetails.product_colors=[dictionary valueForKey:productcolors];
productDetails.product_description=[dictionary valueForKey:productdescription];
productDetails.product_thumb_image_url=[dictionary valueForKey:productThumbImage];
[dModel save];
}
but it shows that you can't insert an NSArray into an NSString, i am struggling to fix this ,
From your question, product_thumb_image_url is a String attribute in Core Data and [dictionary valueForKey:productThumbImage] returns an NSArray of URL strings from your incoming JSON.
So productDetails.product_thumb_image_url=[dictionary valueForKey:productThumbImage]; tries to store an array as a string, which obviously ins't possible.
You either need to store an Array in Core Data, which is done by making the attribute transformable, or you need to store only one image URL, which would be done by taking only the first item from the array (you should check that the array contains some items):
[[dictionary valueForKey:productThumbImage] firstObject]