How to convert hashSet<String> to arrayListOf? - arrays

Using shared preferences to save an arrayListOf in my app. Im converting the array to a hashset and saving it but when I load it I am unable to convert it back to an arrayListOf. I have tried toTypedArray() and toArray() but neither solve the problem. Getting the error below.
Required:
kotlin.collections.ArrayList /* = java.util.ArrayList */
Found:
(Mutable)Set<String!>?
Using the following to try and load the hashet and convert it.
var sharedPreferences = getSharedPreferences(SHARED_PREFS, MODE_PRIVATE)
var places = arrayListOf<String>()
var loadSet = sharedPreferences.getStringSet("key", null)
places = loadSet
go.setOnClickListener{
thread {
val place = newLocation.text.toString()
var stringCounter = counter.toString()
if(place !in places){
places.add(place)
//editor.putStringSet("key", places)
//editor.commit()
var sharedPreferences = getSharedPreferences(SHARED_PREFS, MODE_PRIVATE)
var editor = sharedPreferences.edit()
set.add(place)
editor.putStringSet("key", set);
editor.commit();
Log.d("set", set.toString())
var loadSet = sharedPreferences.getStringSet("key", HashSet<String>())?.toList()
Log.d("load", loadSet.toString())
places = loadSet as ArrayList<String>
}

arrayListOf isn’t a type. It’s a function. The type is ArrayList, but it’s more typical to only need the more abstract List or MutableList, depending on what you’re doing with it. For those you can use set.toList() or set.toMutableList().
If you have an existing ArrayList or MutableList, you could also do list.addAll(set).

Related

Problem on RealmSwift: "Invalid array input: more values (1) than properties (0)." while trying to persist an Array of String

I'm trying to persist in a table view cell, the result of a quiz test with questions and I needed the array of answers given (String Array) so I decided to use RealmSwift.
I created this class and of course I created also a RealmString object in the same file to handle the possibility to persist arrays of String in Realm in this way:
class RealmString: Object {
dynamic var stringValue = ""
}
class Test: Object {
#objc dynamic var ID = UUID().uuidString
#objc dynamic var testScore : String = String()
#objc dynamic var testTitle : String = String()
#objc dynamic var testSubTitle : String = String()
#objc dynamic var dateOfExecution: String = String()
#objc dynamic var answersGiven: [String] {
get {
return _backingAnswersGiven.map { $0.stringValue }
}
set {
_backingAnswersGiven.removeAll()
_backingAnswersGiven.append(objectsIn: (newValue.map({ RealmString(value: [$0]) })))
}
}
let _backingAnswersGiven = List<RealmString>()
override static func ignoredProperties() -> [String] {
return ["answersGiven"]
}
override static func primaryKey() -> String? {
return "ID"
}
Now in the view controller:
I have a variable that stores the result (is an Int array that will take ten answers with values from 0 to 5, and these will later be converted to String)
i.e.: [0,2,2,3,4,5,2,1,0,2] -> ["0","2","2","3","4","5","2","1","0","2"]
and when an option is selected in a question the value is set with this function, everything works fine.
public var questionResults: [Int] = []
func setValueToQuestion(questionNumber: Int) {
questionResults[questionNumber] = optionChosen
}
When the test is completed successfully everything is saved in this way:
let test = Test()
test.ID = currentTest?.ID ?? UUID().uuidString
test.testTitle = testTitleLabel.text!
test.testScore = resultNumberLabel.text!
test.testSubTitle = resultLabel.text!
test.dateOfExecution = dateTimeString
test.answersGiven = questionResults.map({String($0)})
DBManager.sharedInstance.addData(object: test)
I tried the code separately also adding breakpoints and everything works in the flow, expect this line:
test.answersGiven = questionResults.map({String($0)})
that raises the error shown in the title: "Invalid array input: more values (1) than properties (0)."
I guess it can be an error of mapping maybe?
This value is then treated in the rest of flow as a simple swift array of String = [String]
There are a few issues which may be leading to that error.
First the RealmString property is not persisted because it needs #objc
dynamic var stringValue = ""
should be
#objc dynamic var stringValue = ""
Secondly, and this is important, Realm does not support primitives in Lists. Well, it kinda does but not very well.
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
See my answer to this question but in a nutshell, you need another class to store the string in - kind of like your RealmString class.
class StringClass: Object {
#objc dynamic var myString = ""
}
and then change the Test object property to use the StringClass property
#objc dynamic var answersGiven = [StringClass]()
and then I see you're trying to use a backed var and computed property but I am not sure why. It may be simpler to use use the var itself
let _backingAnswersGiven = List<RealmString>()
since the List collection already handles what's being computed.
For example, if you set the list you can set it to another list (which wipes out the current list). Or when you get the list let myStringList = test._backingAnswersGiven, gets all of the StringClasses in the list without having to .map over them.

How can I access an element from an array?

var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1Ow_rhF3sibKL3OAQRXpK6LLZDjMOhW-5DKyWWiN5iZg/edit#gid=638513192');
var data = SpreadsheetApp.setActiveSheet(ss.getSheetByName('Graficos'));
Logger.log(SpreadsheetApp.getActiveSpreadsheet());
var ergo = data.getRange(3,2,4,1);//B3:B6 '
My guess to access the elements was to call var i = ergo[0]; but it didn't work. Do I have to declare ergo using a different syntax?
You need to use getValues() on the range, which will return a 2-dimensional array.
var ergo = data.getRange(3,2,4,1).getValues();
Also, you're not coding efficiently as you're essentially duplicating actions in your first two lines. Take a look at this refactoring:
function test() {
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1Ow_rhF3sibKL3OAQRXpK6LLZDjMOhW-5DKyWWiN5iZg/edit#gid=638513192');
var data = ss.getSheetByName('Graficos');
ss.setActiveSheet(data); // There doesn't seem to be a need to do this, so maybe delete this line
var ergo = data.getRange(3,2,4,1).getValues(); //B3:B6
}

Create an array of Struct arrays?

I have a query that builds a Struct array from some objects. I want to append multiple of these structs into a single array so that i can use it populate a collectionview that is nested inside a collectionview. Im not sure this is the best idea but its all I've got a the moment.
So my Struct is:
struct CollectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
In my viewcontroller the variable to hold the struct is thus:
var packArray : [CollectionStruct] = []
var partArray : [CollectionStruct] = []
var packId = ""
So below what I'm doing is using an item of the previously created packArray to query the DB and build my partArray. Because i want to do this multiple times and append the partArray into a larger parent array i was thinking of using a for loop like:
var i = 0
for item in self.packArray as [AnyObject] {
self.packId = self.packArray[i].id
BuildArray.buildArrayFromQuery(queryForCollection: "Part", selectedPackID: self.packId, delegateSender: "DownloadPart", completeBlock: { (result) in
self.partArray = result
// append this to an array of partArray????
})
}
I don't even know where to start with this. Is it a bad idea?
essentially what i thought was to call this parent array in cellforitem maybe something like:
innerCell.imageCell.image = parentArray[indexPath.item].partArray[indexPath.item].image
but i could be way off.
----- EDIT -----
so i can use a multidimensional array inside the closure once self.partArray is set:
self.partArray = result
var parentArray = [[self.partArray]]
parentArray.append([self.partArray])
but this is unusable outside the closure. How to declare this multidimensional array before self.partArray is set?
------- EDIT -------
Looking into globals for this at the moment.
class GlobalPartArray {
private init() { }
static let sharedInstance = GlobalPartArray()
var globalPartArray = [CollectionStruct]()
}
class GlobalParentArray {
private init() { }
static let sharedInstance = GlobalParentArray()
var globalParentArray = [[GlobalPartArray]]()
}
then inside closure:
GlobalPartArray.sharedInstance.globalPartArray = result
//can not convert [CollectionStruct] to [GlobalPartArray]
GlobalParentArray.sharedInstance.globalParentArray.append(GlobalPartArray.sharedInstance.globalPartArray)
but it throws an error when trying to convert. I obviously need to convert this data somehow before appending to the GlobalParentArray.

ScriptDb, how to tell a object has a key or not

I have such objects in ScriptDb,
[{a:1,b:2,c:3},{a:0,b:0}]
How do I query object without key c?
It seems the only way is to query all object using db.query({}), then use something like "typeof result.c == 'undefined'".
Is there a way to do it in ScriptDb?
Thanks.
You can use that to get records without c:
var db = ScriptDb.getMyDb();
var result = db.query({c: db.not(db.anyValue())});
while (result.hasNext()) {
var current = result.next();
Logger.log ("a= "+current.a+", c="+current.c);
}
The ones with c:
var result = db.query({c: db.anyValue()});
These functions (not, anyValue...) are documented in Class ScriptDbInstance

image array and .src - image not changing

I have created an array which is being used to store a series of .gif images and I'm just trying to test everything out by using document.getElementById to change the .src value but when I change it and load the page the image stays the same as it was before.
function setImage()
{
var images = new Array();
images[0] = anemone.gif;
images[1] = ball.gif;
images[2] = crab.gif;
images[3] = fish2.gif;
images[4] = gull.gif;
images[5] = jellyfish.gif;
images[6] = moon.gif;
images[7] = sail.gif;
images[8] = shell.gif;
images[9] = snail.gif;
images[10] = sun.gif;
images[11] = sunnies.gif;
images[12] = whale.gif;
var slots = new Array();
slots[0] = document.getElementById("slot" + 0);
slots[1] = document.getElementById("slot" + 1);
slots[2] = document.getElementById("slot" + 2);
slots[0].src = "snail.gif";
document.getElementById('slot0').src = images[0];
alert(images.length);
}
I can't understand why the image wont change, but I know it has to be something very simple. I've been wasting hours trying to get this one thing to change but nothing works. can anyone please point out the error of my ways?
There are a couple of issues with your code:
Your filenames need to be Strings, so they'll have to be quoted (also you can simplify the Array creation):
var images = ['anemone.gif', 'ball.gif', 'crab.gif', 'fish2.gif', 'gull.gif', 'jellyfish.gif', 'moon.gif', 'sail.gif', 'shell.gif', 'snail.gif', 'sun.gif', 'sunnies.gif', 'whale.gif'];
Also make sure you are getting your slot-elements right, quote all the attributes like:
<img id="slot0" class="slot" src="crab.gif" width="120" height="80">
When you create the slots-Array you can do it like this (no need to concat the ID string):
var slots = [document.getElementById('slot0'), document.getElementById('slot1'), document.getElementById('slot2')];
Finally make sure you call your function when the document has loaded / the DOM is ready. If you don't want to use a framework like jQuery your easiest bet is probably still using window.onload:
window.onload = setImage; //note that the parens are missing as you want to refer to the function instead of executing it
Further reading on Arrays, window.onload and DOMReady:
https://developer.mozilla.org/de/docs/DOM/window.onload
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
javascript domready?

Resources