Checking multiple values of a dictionary to verify an answer - c#

I managed to create an input field that is able to be checked and verified upon a question via creating another text field and linking that to said question, so for example if the value of the question was '1', the answer would be '1'. Then I made a text comparison, so that if what the user wrote = that text field the answer would be correct.
However, I realise that sometimes someone can write something else. For example in the question ' What do you think about tigers', there isn't just one possible answer. And therefore, the way I did that for the input field does not exactly work (does it?).
I did quite a lot of research and found out about dictionaries, but since they only have one key value that wouldn't help, and then I found out about lists, which might?
So my question is whether it is possible, and how, to create a list that's integer values is somehow linked to the values of the overarching question, so that if the random value is 1, the list value is 1 as well, and then check if what is written matches any of the answers with that random value.
If what I just said didn't make sense, here's an example:
Current behavior:
SURVEY: Do you like cats?
INPUT FIELD: Yes I do
HIDDEN TEXT FIELD : Yes I do
input field = hidden text field and therefore correct
Ideal behavior:
SURVEY: Do you like cats?
INPUT FIELD: I do like cats
POSSIBLE ANSWERS: I do like cats, Yes I do etc.
INPUT FIELD contains an answer in the list which matches the question and therefore correct.
I thought you could use the .Contains function, but I didn't know how to link it all together.
EDIT:
I tried to solve this problem via the creation of a dictionary and searching for a key (which I believe was the right way to do this), but for some reason this code doesn't even work when checking it? (it's like the .containsKey function doesn't work?)
public string questions = "hi;weird;by";
Dictionary<int, string> tester = new Dictionary<int, string>();
// Use this for initialization
void Start ()
{
tester.Add(1, questions);
tester.Add(2, "hello");
tester.Add(3, "by");
tester.Add(4, "hi");
tester.Add(5, "bye");
}
// Update is called once per frame
void Update ()
{
}
public void hello ()
{
if(tester.ContainsKey(2))
{
string value = tester[2];
Debug.Log("Correct");
}
}
EDIT 1:
Following what trashr0X said I tried doing it by having a dictionary script in the main camera and a script in the inputfield, but for some reason when I load it nothing works on the console:
LIST
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
public class Listpractice : MonoBehaviour
{
Dictionary<int, List<string>> tester = new Dictionary<int, List<string>>();
List<string> possibleAnswersToQuestionZero = new List<string>();
// Use this for initialization
void Start () {
possibleAnswersToQuestionZero.Add("Hello");
possibleAnswersToQuestionZero.Add("By");
tester.Add(0, possibleAnswersToQuestionZero);
}
// Update is called once per frame
void Update ()
{
}
public void hello ()
{
var toCheck = tester[0].FirstOrDefault(x => x == GameController.hello);
if (toCheck != null)
{
Debug.Log("Hi!");
}
}
}
INPUT FIELD
public class QAClass07
{
public string Answer = "";
public string Question = "";
QAClass07 result = new QAClass07();
}
public static string hello;
void Start()
{
GameObject a = gameObject;
hello = a.transform.Find("Text").GetComponent<Text>().text;
}
// registers what the user writes
public void getInput(string guess)
{
// Does something assuming someone enters something
if (GetComponent<InputField>() != null)
{
hello = GetComponentInChildren<Text>().text;
}
}

Simply use Dictionary<int, List<string>> and then add all the answers to the corresponding question id.
var questions = new List<string> { "hi", "weird", "by" };
var tester = new Dictionary<int, List<string>>();
// Use this for initialization
void Start ()
{
tester.Add(1, questions);
tester.Add(2, new List<string> { "hello" });
tester.Add(3, new List<string> { "by" });
tester.Add(4, new List<string> { "hi" });
tester.Add(5, new List<string> { "bye" });
}
public void hello ()
{
if(tester.ContainsKey(2))
{
var answers = tester[2] ?? new List<string>();
// now you have all answers linked to question with id 2 in answers variable
}
}

"I did quite a lot of research and found out about dictionaries, but since they only have one key value that wouldn't help, and then I found out about lists, which might?"
Yes, a Dictionary<TKey, TValue> does comprise of key-value pairs of a particular type each; you can declare the type of it's key to be int (corresponding to the index of the question currently asked), and declare the type of it's value to be List<string>, to hold possible answers for that question.
// key is question index, value is a list of possible answers for that question
var dictionary = new Dictionary<int, List<string>>();
// list of possible answers for question 0 (random question number chosen for the example)
var possibleAnswersToQuestionZero = new List<string>();
possibleAnswersToQuestionZero.Add("Possible Answer to question 0");
possibleAnswersToQuestionZero.Add("Another possible answer to question 0");
// add that list to the dictionary at key 0.
// you should be also checking if the key exists before trying to access it's value,
// and what happens if the list returned for that key is null or empty.
dictionary.Add(0, possibleAnswersToQuestionZero);
To check if the answer submitted by the user (let's assume it is saved in a variable named userInput) for question 0 is in the list of possible answers for that key, we would do:
// check if the list at dictionary[0] has at least one instance of userInput,
// otherwise return null
var toCheck = dictionary[0].FirstOrDefault(x => x == userInput);
// if the result is not null, the answer was found
if (toCheck != null)
{
// answer found
}

Related

How to use dictionaries with lists as value c# unity

I am using a dictionary system to store my data and each thing I want to add to my dictionary will need to have 8 definitions. I want to know if anything like that is possible and if yes, I want to learn the codes for adding new items to the dictionary and read those items (if possible read the spesific things from the 8 items inside each list using index)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dictionary_matches : MonoBehaviour
{
/*public static Dictionary<string, List<String>> Matches_dic = new Dictionary<string, List<String>>();
dict.Add("key1", new List<int> { 1, 2, 3 });*/
public static Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("Australia", "Canberra");
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Any help would be appreciated.
It seems, that you are looking for something like this:
public class Dictionary_matches : MonoBehaviour {
public static readonly Dictionary<string, List<string>> Matches_dic =
new Dictionary<string, List<string>>() {
{"match_1", new List<string>() {"a", "b", "c"}},
{"match_2", new List<string>() {"abracadabra"}},
};
public void Update() {
// You can use in any method of the class, e.g.
Matches_dic.Add("more_match", new List<string>() {"p", "q"});
}
// Other staff
}
here we declare the dictionary and with two entries (match_1 and match_2)
When you declare such a dictionary its keys and values are empty.
Let's say I create one such dictionary
var myDict = new Dictionary<string, List<string>>()
Now I want to add a value. But remember, my value is a List<T>.
So you need to check if your key is already present, and if it is already have an instanciated list as a value (e.g. a value != null)
if(myDict.ContainsKey("myKey") && myDict["myKey"] != null)
{
// In this case you could do
mydict["myKey"].Add("myValue");
// Because you already have a list.
// And to read the first item from the list
var firstValue = mydict["myKey"][0]; // or using Linq replace [0] by First()
}
// Now if the key does not exist you need to add the key, and ideally an instantiated list.
if(!myDict.ContainsKey["myKey"])
{
// In your case we know the expected length of the list, so let's specify it.
myDict.Add("myKey", new List<string>(8))
}
You'll notice I didn't write the case where the key would exist, but the value is null.
I'll leave to your consideration if handling that particular case is needed, and if it is, you should have all necessary information in the example above.

how to find number of array list from contains string

how to find number of array list same as contains string, i want make contains string "guess1" get "answer1" and "guess2" get "answer2". how to find n number of array same as contains string?
public class FindContainsText : MonoBehaviour
{
public Text text;
public InputField intext;
List<string> guess = new List<string>();
List<string> answer = new List<string>();
private int n;
void Start()
{
guess.Add("test1");
guess.Add("test2");
answer.Add("answer1");
answer.Add("answer2");
}
// Update is called once per frame
void Update()
{
foreach (string x in guess)
{
if (intext.text.ToLower().Contains(x.ToLower()))
{
text.text = answer[n];
return;
}
}
text.text = "not found";
}
}
Then you should use Dictionary type.
private Dictionary<string, string> guessAnswerDict = new Dictionary<string, string>();
private void Start()
{
guessAnswerDict["test1"] = "answer1";
guessAnswerDict["test2"] = "answer2";
}
You can check whether the test exists in Dictionary with
guessAnswerDict.Contains("test1");
And get the answer value with
var answer = guessAnswerDict["test1"];
It will throw an exception when there are no key in dictionary, so you have to check it with Contains.
Of course you can merge these two with TryGetValue, like
string answer;
guessAnswerDict.TryGetValue("test1", out answer);
// Totally identical!!
guessAnswerDict.TryGetValue("test1", out var answer);
TryGetValue will return false if there are no key in the dictionary.
BTW, although C#'s default access modifier is private, it's always good to explicitly write it's private, which will increase the readability of your code :)

Multiple Nested JSON information - C# Process

apologies if I'm doing something wrong, this is my first post.
I'm currently working with C# and want to save a bunch of data out to a JSON file and load it back, but I'm having trouble figuring out how to get it in the following format.
// Primary ID
001
{
// Secondary ID
01
{
// Tertiary ID
01
{
string: "this is some information.",
int: 9371
}
}
// Secondary ID
02
{
// Tertiary ID
01
{
string: "blah blah blah.",
int: 2241
}
}
}
I'd essentially like to be able to call up information with a particular set of IDs for example 001-02-01 which would return a string ("blah blah blah.") and an int (2241).
The reason I want to go about it like this instead of just having one longer ID is so that when the JSON file becomes very large, I'm hoping to be able to speed up the search for information by passing each ID in turn.
If that makes no sense and it would be equally as fast to just pass in one longer ID and not be bothered by this whole nested ID segments concept then please let me know!
If, however what I'm thinking is correct and it would help the speed of finding particular data by structuring it out like this, how would I go about doing that? With nested C# classes in arrays?
The most simple way and efficient way would be to have all data as same type. Currently, you seem to go for each object is of type of the given id:
{
"01":{},
"02" :{}
}
this will not go too well if trying to use a serializable class.
I would recommend the following:
{
"items" : [
{"id":"01" }, { "id":"02" },...
]
}
Then you can serialize/deserialize easily with
[Serializable]
public class Item
{
public string id = null;
}
[Serializable]
public class RootObject
{
public List<Item> items = null;
}
and then in Unity:
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
}
if you want to speed up the fetching and your collection is large, convert to dictionary.
Dictionary<string, Item> dict = null;
void Start(){
string str = GetJson(); // However you get it
RootObject ro = JsonUtility.FromJson<RootObject>(str);
this.dict = new Dictionary<string,Item>();
foreach(Item item in ro.items){
Item temp = temp;
this.dict.Add(item.Id, temp);
}
ro = null;
}
Now you can access real fast.
Item GetItem(string id)
{
if(string.IsNullOrEmpty(id) == true){ return null; }
Item item = null;
this.dict.TryGetValue(id, out item);
return item;
}
If you end up storing millions of records in your file and want to start doing something more performant it would be easier to switch to a decent document database like MongoDB rather than trying to reinvent the wheel.
Worry about writing good standard code before worrying about performance problems that don't yet exist.
The following example is not in your language of choice but it does explain that JSON and arrays of 1,000,000 objects can be searched very quickly:
const getIncidentId = () => {
let id = Math.random().toString(36).substr(2, 6).toUpperCase().replace("O", "0")
return `${id.slice(0, 3)}-${id.slice(3)}`
}
console.log("Building array of 1,000,000 objects")
const littleData = Array.from({ length: 1000000 }, (v, k) => k + 1).map(x => ({ cells: { Number: x, Id: getIncidentId() } }))
console.log("Getting list of random Ids for array members [49, 60, 70000, 700000, 999999]")
const randomIds = ([49, 60, 70000, 700000, 999999]).map(i => littleData[i].cells.Id)
console.log(randomIds)
console.log("Finding each array item that contains a nested Id property in the randomIds list.")
const foundItems = littleData.filter(i => randomIds.includes(i.cells.Id))
console.log(foundItems)

C# - fastest way of comparing a collection against itself to find duplicates

public class TestObject
{
string TestValue { get; set; }
bool IsDuplicate { get; set; }
}
List<TestObject> testList = new List<TestObject>
{
new TestObject { TestValue = "Matt" },
new TestObject { TestValue = "Bob" },
new TestObject { TestValue = "Alice" },
new TestObject { TestValue = "Matt" },
new TestObject { TestValue = "Claire" },
new TestObject { TestValue = "Matt" }
};
Imagine testList is actually millions of objects long.
What's the fastest way to ensure that two of those three TestObjects with TestValue of Matt gets its IsDuplicate set to true? No matter how may instances of a given value there are, only one should come out of the process with IsDuplicate of false.
I am not averse to doing this via threading. And the collection doesn't have to be a list if converting it to another collection type is faster.
I need to keep duplicates and mark them as such, not remove them from the collection.
To expand, this is (as you might imagine) a simple expression of a much more complex problem. The objects in question already have an ordinal which I can use to order them.
After matching initial duplicates on exact string equality, I'm going to have to go back through the collection again and re-try the remainder using some fuzzy matching logic. The collection that exists at the start of this process won't be changed during the deduplication, or afterwards.
Eventually the original collection is going to be written out to a file, with likely duplicates flagged.
As others mentioned, the correct approach here would be to use the HashSet class.
var hashSet = new HashSet<string>();
foreach (var obj in testList)
{
if (!hashSet.Add(obj.TestValue))
{
obj.IsDuplicate = true;
}
}
When you add a value first time to the HashSet, it adds successfully and HashSet.Add() method returns true so you don't make any changes to the item. When you're trying to add it second time, HashSet.Add() returns false and you mark your item as a duplicate.
The list will have the following state after finishing running our marking duplicates method:
Matt
Bob
Alice
Claire
Matt DUPLICATE
This is probably quite performant:
foreach (var dupe in testList.GroupBy(x => x.TestValue).SelectMany(g => g.Skip(1)))
dupe.IsDuplicate = true;
[EDIT] This method turns out to be about a third of the speed of the accepted answer above, so that one should be used. This answer is merely of academic interest.
Probably I would go to check for the duplicates while building the collection of the TestValue to avoid looping two times on millions of elements. If this scenario is possible then I would use a Dictionary<string, List<TestValue>>
Dictionary<string, List<TestValue>> myList = new Dictionary<string, List<TestValue>>();
while(NotEndOfData())
{
TestValue obj = GetTestValue();
if(myList.ContainsKey(obj.Name))
{
obj.IsDuplicate = true;
myList[obj.Name].Add(obj);
}
else
{
obj.IsDuplicate = false;
myList.Add(obj.Name, new List<TestValue>() { obj};
}
}
SortedSet<string> sorted = new SortedSet<string>();
for (int i = 0; i < testList.Count; i++)
testList[i].IsDuplicate = !sorted.Add(testList[i].TestValue);
As you have allowed in the question, I'd change testList to be an array instead of a list, to make indexer faster.
Since you indicated that you have a property that keeps the ordinal of your items. We can use that property to reset the sort order back to its original after marking our items as duplicates.
The code below is self-explainatory. But just let me know in case you need any further explaination.
I have assumed that the property name is SortOrder. Modify the code accordingly.
void MarkDuplicates()
{
testList = testList.OrderBy(f => f.TestValue).ThenBy(f => f.SortOrder).ToList();
for (int i = 1; i < testList.Count; i++)
{
if (testList[i].TestValue == testList[i - 1].TestValue) testList[i].IsDuplicate = true;
}
testList = testList.OrderBy(f => f.SortOrder).ToList();
}
I'm not a performance expert. But you can time the various solutions provided here and check the performance for yourself.

Value lookup using key or vice versa

First of all, apologies for the nasty title. I will correct it later.
I have some data like below,
"BOULEVARD","BOUL","BOULV", "BLVD"
I need a data structure that is O(1) for looking up any of this words by other. For example, if I use a dictionary I would need to store this keys/values like this, which looks odd to me,
abbr.Add("BLVD", new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" });
abbr.Add("BOUL", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULV", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULEVARD", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
Which data structure to use to keep this data appropriate to my querying terms?
Thanks in advance
Create two HashMap - one maps word to a group number. And the other one maps group number to a list of words. This way you save some memory.
Map<String, Integer> - Word to Group Number
Map<Integer, List<String>> - Group Number to a list of words
You need two O(1) lookups - first to get the group number and then by it - get the list of words.
Assuming abbr is a Dictionary<String, IEnumerable<String>>, you could use the following function:
public static void IndexAbbreviations(IEnumerable<String> abbreviations) {
for (var a in abbreviations)
abbr.Add(a, abbreviations);
}
This will populate the dictionary with the provided list of abbreviations such that when any of them is looked up in the dictionary. It is slightly better than the example code you provided, because I am not creating a new object for each value.
From the documentation, "Retrieving a value by using its key is very fast, close to O(1), because the Dictionary(Of TKey, TValue) class is implemented as a hash table."
The choice of dictionary looks fine to me. As mentioned above, you should use the same list to be referenced in the dictionary. The code could go something like this:
var allAbrList = new List<List<string>>
{
new List<string> {"BOULEVARD", "BOUL", "BOULV", "BLVD"},
new List<string> {"STREET", "ST", "STR"},
// ...
};
var allAbrLookup = new Dictionary<string, List<string>>();
foreach (List<string> list in allAbrList)
{
foreach (string abbr in list)
{
allAbrLookup.Add(abbr, list);
}
}
The last part could be converted into LINQ to have less code, but this way it is easier to understand.
If you don't create a new list for each key, then a Dictionary<string, List<string>> will be fast and reasonably memory-efficient as long as the amount of data isn't enormous. You might also be able to get a little extra benefit from reusing the strings themselves, though the optimizer might take care of that for you anyway.
var abbr = new Dictionary<string, List<string>>;
var values = new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" };
foreach(var aValue in values) abbr.add(value, values);
As Petar Minchev already said, you can split your list into an list of groups and a list of keys that points to this group. To simplify this (in usage) you can write an own implementation of IDictionary and use the Add method to build those groups. I gave it a try and it seems to work. Here are the important parts of the implementation:
public class GroupedDictionary<T> : IDictionary<T,IList<T>>
{
private Dictionary<T, int> _keys;
private Dictionary<int, IList<T>> _valueGroups;
public GroupedDictionary()
{
_keys = new Dictionary<T, int>();
_valueGroups = new Dictionary<int, IList<T>>();
}
public void Add(KeyValuePair<T, IList<T>> item)
{
Add(item.Key, item.Value);
}
public void Add(T key, IList<T> value)
{
// look if some of the values already exist
int existingGroupKey = -1;
foreach (T v in value)
{
if (_keys.Keys.Contains(v))
{
existingGroupKey = _keys[v];
break;
}
}
if (existingGroupKey == -1)
{
// new group
int newGroupKey = _valueGroups.Count;
_valueGroups.Add(newGroupKey, new List<T>(value));
_valueGroups[newGroupKey].Add(key);
foreach (T v in value)
{
_keys.Add(v, newGroupKey);
}
_keys.Add(key, newGroupKey);
}
else
{
// existing group
_valueGroups[existingGroupKey].Add(key);
// add items that are new
foreach (T v in value)
{
if(!_valueGroups[existingGroupKey].Contains(v))
{
_valueGroups[existingGroupKey].Add(v);
}
}
// add new keys
_keys.Add(key, existingGroupKey);
foreach (T v in value)
{
if (!_keys.Keys.Contains(v))
{
_keys.Add(v, existingGroupKey);
}
}
}
}
public IList<T> this[T key]
{
get { return _valueGroups[_keys[key]]; }
set { throw new NotImplementedException(); }
}
}
The usage could look like this:
var groupedDictionary = new GroupedDictionary<string>();
groupedDictionary.Add("BLVD", new List<string> {"BOUL", "BOULV"}); // after that three keys exist and one list of three items
groupedDictionary.Add("BOULEVARD", new List<string> {"BLVD"}); // now there is a fourth key and the key is added to the existing list instance
var items = groupedDictionary["BOULV"]; // will give you the list with four items
Sure it is a lot of work to implement the whole interface but it will give to an encapsulated class that you don't have to worry about, after it is finished.
I don't see a reason to define the value part of your dictionary as a List<string> object, but perhaps that is your requirement. This answer assumes that you just want to know whether the word essentially means "Boulevard".
I would pick one value as the "official" value and map all of the other values to it, like this:
var abbr = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", "BLVD"); // this line may be optional
abbr.Add("BOUL", "BLVD");
abbr.Add("BOULV", "BLVD");
abbr.Add("BOULEVARD", "BLVD");
Alternatively, you could define an enum for the value part of the dictionary, as shown below:
enum AddressLine1Suffix
{
Road,
Street,
Avenue,
Boulevard,
}
var abbr = new Dictionary<string, AddressLine1Suffix>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", AddressLine1Suffix.Boulevard);
abbr.Add("BOUL", AddressLine1Suffix.Boulevard);
abbr.Add("BOULV", AddressLine1Suffix.Boulevard);
abbr.Add("BOULEVARD", AddressLine1Suffix.Boulevard);

Categories