I have a DumpContainer to which I'd like to add content dynamically. I know I can set the Content property to a new object but that gets rid of the previous content.
How can I add instead of replace?
Thanks for your help.
A couple more options:
If you want the items displayed vertically, but not in a table, use Util.VerticalRun inside a DumpContainer:
var content = new List<object>();
var dc = new DumpContainer (Util.VerticalRun (content)).Dump();
content.Add ("some text");
dc.Refresh();
content.Add (new Uri ("http://www.linqpad.net"));
dc.Refresh();
If the items are generated asynchronously, use an asynchronous stream (C# 8):
async Task Main()
{
// LINQPad dumps IAsyncEnumerables in the same way as IObservables:
RangeAsync (0, 10).Dump();
}
public static async IAsyncEnumerable<int> RangeAsync (int start, int count)
{
for (int i = start; i < start + count; i++)
{
await Task.Delay (100);
yield return i;
}
}
If you are putting general objects in the DumpContainer that are displayed with field values, etc. you could convert to a similar (but without the Display in Grid button) format with Util.ToHtmlString and then concatenate.
var dc = new DumpContainer();
dc.Dump();
dc.Content = Util.RawHtml(Util.ToHtmlString(true, aVariable));
// some time later...
dc.Content = Util.RawHtml(Util.ToHtmlString(dc.Content)+Util.ToHtmlString(true, anotherVar));
Instead of adding content to the DumpContainer, go ahead and update it, but make its contents be something that has all the data you're trying to add.
var c = new DumpContainer();
var list = new List<string> {"message 1"};
c.Content = list;
c.Dump();
list.Add("message 2");
c.UpdateContent(list);
Alternatively, you can dump an IObservable<>, and it'll automatically get updated as objects are emitted. (Import the System.Reactive NuGet package to make this work.)
var s = new Subject<string>();
s.Dump();
s.OnNext("message 1");
s.OnNext("message 2");
Related
So, I want to store the inventory of the player in Firebase Realtime Database.
I set up a basic schema, and I want to add the items (their names, actually) under the inventory "branch".
I am trying to do it with the push method, but it ain't working for me right now.
I got the following script which updates the player's inventory client-side and should update the database to, with appending the inventory "branch".
What am I doing wrong?
public void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(items[i].item_name).Push();
lastIndex = i;
}
}
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
I'm guessing that there's slight confusion around what Push() does in this context. If you read the documentation, it says:
Add to a list of data. Every time you call Push(), Firebase generates a unique key that can also be used as a unique identifier, such as user-scores/<user-id>/<unique-score-id>.
Basically, it's giving you a place you can safely write into rather than actually adding something to the Database. You should check out the sample, but I think I can illustrate both what you intend to do and a cool way to use Push() for your use case.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").SetValueAsync(itemNames);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
What I'm doing here is that I build a list of the items before hand. Then I set the inventory entry to this list. If I depended on the previous state, I'd probably prefer to do this as a transaction instead.
I also use async/await. I'm not sure how important it is that this logic runs before you update your game's UI, but setting anything in Firebase is an asynchronous operation (it runs in the background). You can omit this if it's not important that you stay in sync.
If you wanted to have uniquely identified weapon slots. You could instead do something like:
var childReference = reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory");
var key = childReference.Key;
// ... more logic here ...
childReference.Child(key).SetValueAsync(item_name);
A few more hints:
I'd recommend that you use the ValueChanged listener whenever possible to keep your game state in sync with your server state. Realtime Database will always be running asynchronously in the background, and this way you can help stay up to date as more data becomes available.
If asynchronous programming in Unity is new to you, check out my post on the subject.
Okay, so I've finally figured it out.
With the help of Push() method's unique id generation, I am able to add childs with the same item name to the inventory node.
Firstly, I create that unique id, then, I add that unique id to the database and set it's value to the item name.
Edit: I mixed my original solution with Pux0r3's solution, so that it uses async/await.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if (free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
string key = reference.Child(auth.CurrentUser.UserId).Child("inventory").Push().Key;
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(key).SetValueAsync(currentPickable.item_name);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
How can I ban a variable from a list without removing it from that list by adding the variable to a list of "banned" variable?
I wish to be able to type in a string. That string is compared to the file names in a folder. If there is a match, the file is read. If I type this same string again, the file should not be read again. There for I want to have a list of "banned" string that is checked whilst typing to avoid the file to be read again.
I have tried a few ways but not getting there. Below is an example of my last attempt.
What would be the best way?
public class test
{
string scl= "test3";
List <string> lsf,lso;
void Start ()
{
lsf=//file names
new List<string>();
lso=//files open
new List<string>();
lsf.Add("test0");
lsf.Add("test1");
lsf.Add("test2");
lsf.Add("test3");
lsf.Add("test4");
lso.Add("idhtk49fngo");//random string
}
void Update ()
{
if
(
Input.GetKeyDown("a")
)
{
for
(
int i=0;
i<lsf.Count;
i++
)
{
if(lsf[i]==scl)
{
Debug.Log
(i+" is read");
for
(
int j=0;
j<lso.Count;
j++
)
{
//how can i avoid reading
//lsf[3] here the second time
//"a" is pressed (by having "test3"
//added to a "ban" list (lso) )
if(scl!=lso[j])
{
lso.Add(lsf[i]);
}
}
}
}
}
}
Michael’s answer is the way to go here but it can be improved using the more appropriate collection available to keep track of opened files; if you want uniqueness use a set, not a list:
HashSet<string> openedFiles = new HashSet<string>();
public static bool TryFirstRead(
string path,
out string result)
{
if (openedFiles.Add(path))
{
result = File.ReadAllText(path);
return true;
}
result = null;
return false;
}
Also, I’d avoid throwing vexing exceptions. Give the consumer a friendly way to know if the file was read or not, don’t make them end up having to use exceptions as a flow control mechanism.
I didn't understand although if you want to replace a value from another list.
You can use the list index to create a new list with the values which you removed.
String list1 = {"hi", "hello", "World"};
String list2 = {"bye", "goodbye", "World"};
List1[1] = list2[1];
I would suggest such way:
public static List<string> openedFiles = new List<string>();
public static string ReadFileAndAddToOpenedList(string path)
{
if (openedFiles.Contains(path))
throw new Exception("File already opened");
// Instead of throwing exception you could for example just log this or do something else, like:
// Consolle.WriteLine("File already opened");
else
{
openedFiles.Add(path);
return File.ReadAllText(path);
}
}
The idea is - on every file read, add file to list, so you can check every time you try read file, if it was already read (or opened). If it is, throw exception (or do something else). Else read a file.
You could instead of making it a string list use your own class
public class MyFile
{
public string Name;
public bool isOpen;
public MyFile(string name)
{
Name = name;
isOpen = false;
}
}
List<MyFile> lsf = new List<MyFile>()
{
new MyFile("test0"),
new MyFile("test1"),
new MyFile("test2"),
new MyFile("test3"),
new MyFile("test4")
};
Than when you read the file set isOpen to true
MyFile[someIndex].isOpen = true;
and later you can check this
// E.g. skip in a loop
if(MyFile[someIndex]) continue;
You could than also use Linq in order to get a list of only unread files:
var unreadFiles = lsf.Select(f => f.Name).Where(file => !file.isOpen);
I have block of code which contains a lot of nested loops (I have to mention over here is that we don't have a problem with speed of the code). Now in turn on that I have to have the same functionality within the same Controller Action with all the loops again, but I don't want to just copy and paste the same code and just changing the variables over there.
The idea of the code is with the first For loop we are making requests over some API and if the response is empty we are stop sending request. Inside the loop we are reading JSON response of every calls and add it to our class:
var transactions = new List<Transaction>();
for (int i = 0; i < pages; i++)
{
if (morePagesInXero == false)
{
valueCollection.Add("page", pages.ToString());
morePages= true;
}
else
{
valueCollection.Set("page", pages.ToString());
}
var invoices = api.GetArray(GlobalConstants.Invoices, where, null, valueCollection);
dynamic invoicesResponse=JObject.Parse(invoices.ToString());
var jsonTransactions =((JProperty((JContainer)invoicesResponse).Last).Value;
var arrayTransactions = ((JArray)jsonTransactions);
// Check if there are more than 1 page
if (arrayTransactions.Count == 0)
{
break;
}
else
{
foreach (var item in jsonTransactions)
{
transaction.taxDirection = "DummyDataFromResponseJSON";
transaction.invoiceNumber = "DummyData1FromResponseJSON"
transaction.upstreamModifiedAt = "DummyData2FromResponseJSON";
transaction.currency = "DummyData3FromResponseJSON";
transaction.customerId = "DummyData4FromResponseJSON";
// and so on ...
transactions.Add(transaction);
}
pages++;
}
Now I have to make a new call to the API on different end point but the logic of manipulating the JSON response is the same one with those loops. Please advice is it possible to reuse the code above in order to make it more generic and just reuse it.
Thanks in advance.
Problem code:
for (int i = 0; i <= numberOfPlayers - 1; i++)
{
if (i == dealerPosition())
{
StringBuilder sb = new StringBuilder();
// e.g. outputs "tbPosition1"
sb.Append("tbPosition").Append(dealerPosition().ToString());
// The following line of code does not work as sb is a string containing
// "thPosition1", not my controller tbPosition1. How do I fix this?
Dispatcher.Invoke(() => { (sb.Text = dealerPosition().ToString(); });
break;
}
}
Using C#, WPF, Visual Studio.
sb.Append("tbPosition").Append(dealerPosition().ToString());
// The following line of code does not work as sb is a string containing
// "thPosition1", not my controller tbPosition1. How do I fix this?
Dispatcher.Invoke(() => { ((this.FindName(sb.ToString()) as TextBox).Text = dealerPosition().ToString(); });
From Complexity's comment though, the post mentions that you can add all of your elements into the list and loop / foreach that when you want to work on it:
List<TextBox> textBoxesToEdit = new List<TextBox>(){tbposition1, tbposition2 /*so on*/};
foreach (TextBox textbox in textBoxesToEdit)
{
//do stuff
}
FindName() should help you with this, but note that you may have to may have to register each control as you create it if you add them after the control is initially created.
In line with your answer to my quetion I would store my tdPositionXYZ in an array or List and do this instead of all of your code:
Dispatcher.Invoke(() => {
tbPositionArray[dealerPosition()].Text = dealerPosition().ToString();
});
I'm building an app for windows 8 desktop, I'm reading in a text file and I want to change one specific line but not sure how so what I have is a text file that says
username|false
username|false
username|false
And I want to remove the middle line when something happens, this is what I have so far;
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile storageFile = await folder.GetFileAsync("students.txt");
var text = await Windows.Storage.FileIO.ReadLinesAsync(storageFile);
var list_false = "";
foreach (var line in text)
{
string name = "" + line.Split('|')[0];
string testTaken = "" + line.Split('|')[1];
if (your_name.Text == name)
{
if (testTaken == "false") {
pageTitle.Text = name;
enter_name_grid.Opacity = 0;
questions_grid.Opacity = 1;
var md = new MessageDialog("Enjoy the test");
await md.ShowAsync();
}
else
{
the_name.Text = "You have already taken the test";
var md1 = new MessageDialog("You have already taken the test");
await md1.ShowAsync();
}
return;
}
else
{
list_false = "You're not on the list";
}
}
if (list_false == "You're not on the list") {
var md2 = new MessageDialog("You're not on the list");
await md2.ShowAsync();
}
Help please, it reads in names perfectly and allows them to take the test, I just need it to remove the correct line. Thanks in advance!!
The important thing to consider is that you are modifying a file. So whatever you choose to change then you need to write it back to the file.
In your case you are opting to read the whole file into memory, this actually works in your favor for something like this as you can just remove any unwanted lines and write back to the file. However, you cannot remove an item while you are iterating through the list using a foreach loop.
The best practice for removing items from an array you are looping is to use a for loop and loop in reverse. It also makes it easier to remove items if we work with a List<string> too, like so:
var list = new List<string>(text);
for(int i = text.Length - 1; i >=0; i--)
{
string line = text[i];
//rest of code
}
text = list.ToArray();
The next part of your task is to remove the line. You can do this in your else statement as this is the part that handles users already having taken the test. For example:
the_name.Text = "You have already taken the test";
list.RemoveAt(i);
Finally, after your loop you need to write the whole thing back to the file:
await Windows.Storage.FileIO.WriteLinesAsync(storageFile, text);
When you read the file, you could store the contents in a list. When your "something happens" you could remove the content at the appropriate index and save (overwrite) the list to the file.