I have the below code:
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
}
}
ticketStoreNew = ticketStore;
return output;
}`
It takes a dictionary, ticketStore, checks to see if it has any new elements not in ticketStoreNew and puts them in the output dictionary. Then, ticketStoreNew is set to ticketStore until ticketStore is updated with another method and this method is ran again.
However, when I include the line ticketStoreNew = ticketStore, the program returns an empty dictionary. It looks like the method is not executing sequentially and this is running prior to the for loop.
I really just need to return any new items added to the ticketStore dictionary.
EDIT
Below is the code for getting ticketStore:
public void UpdateTickets(string inputXml)
{
// If no new tickets exit
if (inputXml.Trim() == "") { return; }
//xmlString = inputXml;
// Load XML into an enumerable
XElement xelement = XElement.Parse(inputXml);
IEnumerable<XElement> xml = xelement.Elements();
foreach (var item in xml)
{
if (item.Name == "incident")
{
int id;
// If ID can be converted to INT
if (Int32.TryParse(item.Element("id").Value, out id))
{
// If ticket is not already in store create ticket and populate data
if (!ticketStore.ContainsKey(id))
{
Ticket ticket = new Ticket();
ticket.id = id;
ticket.number = Int32.Parse(item.Element("number").Value);
ticket.title = item.Element("name").Value;
ticket.description = item.Element("description").Value;
ticketStore.Add(id, ticket);
}
}
}
}
}
}
The tickets are all based on getting XML from the Samanage API.
If another method updates ticketStore then the assignment is the problem. It doesn't copy the contents of ticketStore to ticketStoreNew it sets the reference ticketStoreNew to point to the same instance as ticketStore. Thus they are the same object and always have the same contents. Try creating a new Dictionary to copy the items:
ticketStoreNew = new Dictionary<int, Ticket>(ticketStore);
Try this code:
private Dictionary<int, Ticket> ticketStoreNew =
new Dictionary<int, Ticket>(); // add this line
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
ticketStoreNew.Add(item.Key, item.Value); // add this line
}
}
//ticketStoreNew = ticketStore; remove this line
return output;
}
Related
I have read many posts about dictionaries and serialization etc. and they all have different ways of doing things.
Being new to coding (to an extent) I am having trouble figuring out how to get my dictionary to a list or serialized to get it saved then reloaded on play. This is Unity BTW.
So here is what I am using.
I have an application manager that is mainly just a set of BOOL values that tells my mission system if something has been completed or not. Something like "GetWeapon FALSE" (then when they pick it up it changes to TRUE which tells the mission system to move to the next objective or complete)
So I have a starting list of keys,values...these then get placed into a dictionary and the values are changed within that.
I need to be able to save those values and reload them on LOAD (default PLAY mode is newgame--as you see below it resets the dictionary and copies in the starting states). I know it can't be as difficult as I am making it, just not understanding the whole serialize thing.
Most sites are showing a dictionary like {key,value} so I am getting lost on iterating through the dictionary and capturing the pairs and saving them.
Here is partial code for the appmanager (it is a singleton):
// This holds any states you wish set at game startup
[SerializeField] private List<GameState> _startingGameStates = new List<GameState>();
// Used to store the key/values pairs in the above list in a more efficient dictionary
private Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>();
void Awake()
{
// This object must live for the entire application
DontDestroyOnLoad(gameObject);
ResetGameStates();
}
void ResetGameStates()
{
_gameStateDictionary.Clear();
// Copy starting game states into game state dictionary
for (int i = 0; i < _startingGameStates.Count; i++)
{
GameState gs = _startingGameStates[i];
_gameStateDictionary[gs.Key] = gs.Value;
}
OnStateChanged.Invoke();
}
public GameState GetStartingGameState(string key)
{
for (int i = 0; i < _startingGameStates.Count; i++)
{
if (_startingGameStates[i] != null && _startingGameStates[i].Key.Equals(key))
return _startingGameStates[i];
}
return null;
}
// Name : SetGameState
// Desc : Sets a Game State
public bool SetGameState(string key, string value)
{
if (key == null || value == null) return false;
_gameStateDictionary[key] = value;
OnStateChanged.Invoke();
return true;
}
Tried something similar to this:
Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>
{
for (int i = 0; i < _gameStateDictionary.Count; i++)
string json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
Debug.Log(json);
{
}
However all I got was the first item in the list. (I did modify the above in a for loop) I know the above is wrong, I did other iterations to just to get the dictionary to print out in the console.
Anyway, just asking for a little code help to save and load a dictionary.
Since you want to save and load those persistent anyway, personally I would not use the Inspector and bother with Unity serialization at all but rather directly serialize/deserialize from and to json.
Simply place your default dictionary as a simple json file like e.g.
{
"Key1" : "Value1",
"Key2" : "Value2",
"Key3" : "Value3",
...
}
within the folder Assets/StreamingAssets (create it if it doesn't exists). This will be your default fallback file if none exists so far.
On runtime you will load it from Application.streamingAssetsPath.
Then when saving you will not put it there but rather to the Application.persistentDataPath
using Newtonsoft.Json;
...
[SerializeField] private string fileName = "example.json";
private string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
private Dictionary<string, string> _gameStateDictionary
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void Save()
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.intended);
File.WriteAllText(filePath, json);
}
If you then still want to edit the default via the Inspector you could merge both and instead of using StreamingAssets do
[SerializeField] private GameState[] defaultStates;
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
LoadDefaults();
return;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void LoadDefaults()
{
_gameStateDictionary = defaultStates.ToDictionary(gameState => gameState.Key, gameState => gameState.Value);
}
And then btw you could do
public string GetState(string key, string fallbackValue = "")
{
if(_gameStateDictionary.TryGetValue(key, out var value))
{
return value;
}
return fallbackValue;
}
You can iterate the dictionary by using its Keys collection.
foreach (var key in myDictionary.Keys)
{
var val = myDictionary[key];
}
Populate your data structure with the key/value pairs and serialize the list of items.
var missionItems = new List<MissionItem>();
foreach (var key in myDictionary.Keys)
{
var missionItem = new MissionItem();
missionItem.Key = key;
missionItem.Value = myDictionary[key];
missionItems.Add(missionItem);
}
var json = JsonUtility.ToJson(missionItems);
Loading is straight forward, deserialize your data, then loop through all the items and add each one to the empty dicitionary.
var missionItems = JsonUtility.FromJson<List<MissionItem>>(json);
foreach (var item in missionItems)
{
myDictionary.Add(item.Key, item.Value);
}
Here is my final setup.
I call these on button press in another script (the UI pause menu save/load buttons)
private bool useEncryption = true;
private string encryptionCodeWord = "CodeWordHere";
private static string fileName = "name.json";
private static string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private static string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
public void SaveStates(int slotIndex)
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
File.WriteAllText(filePath + slotIndex, json);
}
public void LoadStates(int SlotIndex)
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath + SlotIndex))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath + SlotIndex);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
private string EncryptDecrypt(string data)
{
string modifiedData = "";
for (int i = 0; i < data.Length; i++)
{
modifiedData += (char)(data[i] ^ encryptionCodeWord[i % encryptionCodeWord.Length]);
}
return modifiedData;
}
I'm trying to parse huge json file to 2d array.
I can parse. But required memory is almost 10times.
My sample.json file has 100,000 rows, each with a different item.
If sample.json is 500MB this code need 5GB.
How can i reduce memory usage?
I use Newtonsoft.Json, .Net6.0
Read from json
static void Read()
{
List<Dictionary<string, string>> rows = new List<Dictionary<string, string>>();
string path = #"D:\small.json";
using (FileStream fsRead = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bsRead = new BufferedStream(fsRead))
using (StreamReader srRead = new StreamReader(bsRead))
{
string? line;
while ((line = srRead.ReadLine()) != null)
{
JObject jsonObject = JObject.Parse(line);
MakeRowData(jsonObject, out var row);
rows.Add(row);
}
}
}
Make row
private static void MakeRowData(JObject jsonData, out Dictionary<string, string> row)
{
Dictionary<string, string> output = new Dictionary<string, string>();
foreach (var item in jsonData)
{
int childSize = 0;
if (item.Value != null)
{
childSize = item.Value.Children().Count();
///if Item has child, explore deep
if (childSize > 0)
{
ExploreChild(item.Value, ref output);
}
///or not just add new item
else
{
string str = item.Value.ToString();
output[item.Key] = str ?? "";
}
}
}
row = output;
}
private static void ExploreChild(JToken jToken, ref Dictionary<string, string> row)
{
foreach (var item in jToken)
{
int childSize = item.Children().Count();
///if Item has child, explore deep
if (childSize > 0)
{
ExploreChild(item, ref row);
}
///or not just add new item
else
{
string path = jToken.Path.Replace('[', '(').Replace(']', ')');
string str = jToken.First.ToString();
row[path] = str?? "";
}
}
}
EDIT
Add Sample.json
It is set of json strings.
And Fields are not fixed.
Sample.json
{Field1:0,Field2:1,Field2:3}
{Field1:0,Field5:1,Field6:3}
{Field1:0,Field7:1,Field9:3}
{Field1:0,Field13:1,Field50:3,Field57:3}
...
You can try replacing the recursive exploring children with the iterative one. Something like this:
private static void MakeRowData(JObject jsonData, out Dictionary<string, string> row)
{
Dictionary<string, string> output = new Dictionary<string, string>();
foreach (var item in jsonData)
{
if (item.Value != null)
{
///if Item has child, explore deep
if (item.Value.HasValues)
{
var queue = new Queue<JToken>();
queue.Enqueue(item.Value);
while (queue.Any())
{
var currItem = queue.Dequeue();
if (currItem.HasValues)
{
foreach(var child in item)
queue.Enqueue(child);
}
else
{
// add item without children to row here
}
}
}
///or not just add new item
else
{
string str = item.Value.ToString();
output[item.Key] = str ?? "";
}
}
}
row = output;
}
Recursive calls, unless it is a tail recursion, keep the stack of a method they were called from. This can lead to extensive memory usage.
I have a List (from Newtonsoft), and I'm trying to define a DisplayValue for Microsoft .NET list box.
List<JToken> gCollectionRequests = new List<JToken>();
//Code here to create a valid gCollectionRequests
listBox1.DataSource = gCollectionRequests;
listBox1.DisplayMember = gCollectionRequests[0]["name"].Value<string>();
The List is based on a JSON Postman collection file, whose first field is "name". That's what I want to display. The line line above doesn't break the code, it just doesn't have any effect.
An example of the gCollectionRequests:
What is the right way to define the DisplayValue from List source?
Thanks, #SriramSakthivel. That was the key. So I made a new object that incorporated my JSON collection. Is this how you would have done it?
public class CombinedJTokens
{
private int RequestCount; //Not really needed.
public JToken MyRequest;
public CombinedJTokens(int myCount, JToken myToken)
{
this.RequestCount = myCount;
this.MyRequest = myToken;
}
public string DisplayName
{
get { return string.Format("This is a JToken: {0}", MyRequest["name"].Value<string>()); }
}
}
Then, this handles filling in the listbox:
private void FillInListBoxWithRequests()
{
List<CombinedJTokens> myCombinedTokensList = new List<CombinedJTokens>();
int theCount = 0;
foreach (var item in gCollectionRequests)
{
JToken myNewJToken = item;
CombinedJTokens myCombinedJTokens = new CombinedJTokens(++theCount, item);
myCombinedTokensList.Add(myCombinedJTokens);
}
listBox1.DataSource = null;
listBox1.DataSource = myCombinedTokensList;
listBox1.DisplayMember = "DisplayName";
}
gCollectionRequests is a list of the JSON requests defined as:
List<JToken> gCollectionRequests = new List<JToken>();
I have problems with transactions using Firebase. I want to update 2 nodes (models and scenes) at the same time but it never updates my database.
Here is my code:
public static void Delete(DataContainers.Scene iScene)
{
DatabaseReference leaderBoardRef = FirebaseDatabase.DefaultInstance.RootReference;
leaderBoardRef.RunTransaction(mutableData =>
{
mutableData.Child("Scenes/" + iScene.id).Value = null;
foreach (var _model in iScene.models)
{
mutableData.Child("models/" + _model).Value = null;
}
return TransactionResult.Success(mutableData);
});
}
Is there any other way how to do it?
You can use a multi-path update to delete all the data at once like you desire:
private void deleteScene(DataContainers.Scene iScene) {
// Get root database reference
DatabaseReference mDatabase = FirebaseDatabase.DefaultInstance.RootReference;
// Initialize a new list of "path->value" pairs
Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
// Delete the given scene and all of its models
childUpdates["/Scenes/" + iScene.id] = null;
foreach (var _model in iScene.models)
{
childUpdates["/models/" + _model] = null;
}
mDatabase.UpdateChildrenAsync(childUpdates);
}
I'm trying to make a parser which will read a CSV file and add information in a List.
My problem is that when I make my loop to add in a Dictionary, the header(key) and the value(value), I have a error
"Impossible to cast void in List" (translated from French).
Code:
private List<string> header = null;
private List<string> tableauValeurs = null;
public bool ParseCSVFile(string csvPath)
{
bool result = false;
if (File.Exists(csvPath))
{
using (StreamReader sr = new StreamReader(csvPath))
{
var firsLine = sr.ReadLine();
this.header = firsLine.Split(';').ToList();
while (sr.Peek() >= 0)
{
var line = sr.ReadLine().Split(';');
this.tableauValeurs = new List<string>();
Dictionary<string, List<string>> lineDico = new Dictionary<string, List<string>>();
for (int i = 0; i < this.header.Count; i++)
{
lineDico.Add(this.header[i], ***this.tableauValeurs.Add(line[i]***);
}
}
result = true;
}
}
return result;
}
Any idea? Thanks
The method Add is a void method. It doesn't return a new list.
So, add your element into the list first, then put the list into the Dictionary.
this.tableauValeurs.Add(line[i]);
lineDico.Add(this.header[i], this.tableauValeurs);
Add returns void. You need to add your line to tableauValeurs first and then add tableauValeurs to your Dictionary
You have:
lineDico.Add(this.header[i], this.tableauValeurs.Add(line[i]));
Now; lineDico is Dictionary<string, List<string>>, so the Add method wants a string and a List<string>; this.header[i] looks fine, but this.tableauValeurs.Add(line[i]) will return void, which isn't a List<string>. So: what is the List<string> that you intended to add as the value in lineDico?
this.tableauValeurs.Add(line[i]) doesnt return a list.
Probably best to use: (no need of this if field is only in this class)
tableauValeurs.Add(line[i]));
lineDico.Add(header[i], tableauValeurs);
If you wish to have a new list associated with dictionary, move the new list declaration after for loop.