Fast way to check if dictionary contains certain value, like
"dictionary.contains(value)" in Java. Ex:
var dict:[Int:String?] = [1:"hello", 2:"world"]
How can I check if the dictionary contains "world"?
Your only option is to iterate on each dictionary element to find if that value is present. There are multiple ways to do that, but they are all linear-time.
One such way would be:
foreach (key, value) in dict {
if value == "world" {
print("Found world")
}
}
Another (shorter) way would be:
dict.values.contains("world")
Related
I have a project that uses arrays of objects that I'm thinking of moving to es6 Sets or Maps.
I need to quickly get a random item from them (obviously trivial for my current arrays). How would I do this?
Maps and Sets are not well suited for random access. They are ordered and their length is known, but they are not indexed for access by an order index. As such, to get the Nth item in a Map or Set, you have to iterate through it to find that item.
The simple way to get a random item from a Set or Map would be to get the entire list of keys/items and then select a random one.
// get random item from a Set
function getRandomItem(set) {
let items = Array.from(set);
return items[Math.floor(Math.random() * items.length)];
}
You could make a version that would work with both a Set and a Map like this:
// returns random key from Set or Map
function getRandomKey(collection) {
let keys = Array.from(collection.keys());
return keys[Math.floor(Math.random() * keys.length)];
}
This is obviously not something that would perform well with a large Set or Map since it has to iterate all the keys and build a temporary array in order to select a random one.
Since both a Map and a Set have a known size, you could also select the random index based purely on the .size property and then you could iterate through the Map or Set until you got to the desired Nth item. For large collections, that might be a bit faster and would avoid creating the temporary array of keys at the expense of a little more code, though on average it would still be proportional to the size/2 of the collection.
// returns random key from Set or Map
function getRandomKey(collection) {
let index = Math.floor(Math.random() * collection.size);
let cntr = 0;
for (let key of collection.keys()) {
if (cntr++ === index) {
return key;
}
}
}
There's a short neat ES6+ version of the answer above:
const getRandomItem = iterable => iterable.get([...iterable.keys()][Math.floor(Math.random() * iterable.size)])
Works for Maps as well as for Sets (where keys() is an alias for value() method)
This is the short answer for Sets:
const getRandomItem = set => [...set][Math.floor(Math.random()*set.size)]
I am trying to create an empty map, that will be then populated within a for loop. Not sure how to proceed in Rascal. For testing purpose, I tried:
rascal>map[int, list[int]] x;
ok
Though, when I try to populate "x" using:
rascal>x += (1, [1,2,3])
>>>>>>>;
>>>>>>>;
^ Parse error here
I got a parse error.
To start, it would be best to assign it an initial value. You don't have to do this at the console, but this is required if you declare the variable inside a script. Also, if you are going to use +=, it has to already have an assigned value.
rascal>map[int,list[int]] x = ( );
map[int, list[int]]: ()
Then, when you are adding items into the map, the key and the value are separated by a :, not by a ,, so you want something like this instead:
rascal>x += ( 1 : [1,2,3]);
map[int, list[int]]: (1:[1,2,3])
rascal>x[1];
list[int]: [1,2,3]
An easier way to do this is to use similar notation to the lookup shown just above:
rascal>x[1] = [1,2,3];
map[int, list[int]]: (1:[1,2,3])
Generally, if you are just setting the value for one key, or are assigning keys inside a loop, x[key] = value is better, += is better if you are adding two existing maps together and saving the result into one of them.
I also like this solution sometimes, where you instead of joining maps just update the value of a certain key:
m = ();
for (...whatever...) {
m[key]?[] += [1,2,3];
}
In this code, when the key is not yet present in the map, then it starts with the [] empty list and then concatenates [1,2,3] to it, or if the key is present already, let's say it's already at [1,2,3], then this will create [1,2,3,1,2,3] at the specific key in the map.
Suppose I have a certain value, and I want to do something with it depending on certain characteristics it might have.
For example, suppose the value is a string, and I want to print it to the screen if it starts with the letter L, save it to a file if it's length is less than 20 characters, and play a sound if the last character is the same as the first one.
One option of course is a simple if else if construct:
if (value[0] == 'L')
....
else if (value.Length < 20)
....
else if (value[0] == value.Last())
....
However with a lot of conditions, this can get ugly really fast. So the other option is a Dictionary. However I'm not sure how I can use a Dictionary to achieve this.
How can this be done?
You can construct a dictionary that contains conditions and actions that should be performed if a condition is met. In general, if you need to work with type T, this dictionary will have a type Dictionary<Predicate<T>, Action<T>>. For a string it can be:
var conditions = new Dictionary<Predicate<string>, Action<string>>
{
{s => s.StartsWith("L"), s => Console.WriteLine("Starts with L")},
{s => s.Length < 20, s => Console.WriteLine("Has fewer that 20 symbols")},
};
string input = "some input";
foreach (var condition in conditions)
{
if (condition.Key(input)) condition.Value(input);
}
In fact, you don't even need a Dictionary here - you can use List<Tuple<Predicate<string>, Action<string>>>, or, even better - to introduce a simple small class that contains a predicate and an action.
I'm messing around a bit with F# and I'm not quite sure if I'm doing this correctly. In C# this could be done with an IDictionary or something similar.
type School() =
member val Roster = Map.empty with get, set
member this.add(grade: int, studentName: string) =
match this.Roster.ContainsKey(grade) with
| true -> // Can I do something like this.Roster.[grade].Insert([studentName])?
| false -> this.Roster <- this.Roster.Add(grade, [studentName])
Is there a way to insert into the map if it contains a specified key or am I just using the wrong collection in this case?
The F# Map type is a mapping from keys to values just like ordinary .NET Dictionary, except that it is immutable.
If I understand your aim correctly, you're trying to keep a list of students for each grade. The type in that case is a map from integers to lists of names, i.e. Map<int, string list>.
The Add operation on the map actually either adds or replaces an element, so I think that's the operation you want in the false case. In the true case, you need to get the current list, append the new student and then replace the existing record. One way to do this is to write something like:
type School() =
member val Roster = Map.empty with get, set
member this.Add(grade: int, studentName: string) =
// Try to get the current list of students for a given 'grade'
let studentsOpt = this.Roster.TryFind(grade)
// If the result was 'None', then use empty list as the default
let students = defaultArg studentsOpt []
// Create a new list with the new student at the front
let newStudents = studentName::students
// Create & save map with new/replaced mapping for 'grade'
this.Roster <- this.Roster.Add(grade, newStudents)
This is not thread-safe (because calling Add concurrently might not update the map properly). However, you can access school.Roster at any time, iterate over it (or share references to it) safely, because it is an immutable structure. However, if you do not care about that, then using standard Dictionary would be perfectly fine too - depends on your actual use case.
I want to see if a variable exists - i.e. that I have created in.
if(exists(this.mydict))
{ //append my dict
}else
{
// initialize dict
}
Trouble is this fails on
Error in exists(this.mydict)
What am I doing wrong?
How can I extend the exists function to work with the following:
Any ideas how I would extend to this to looking at seeing whether a nested dictionary would also exist. I.e. for example: if(exists("mylists[[index]]['TSI']")), where the mylists object is a dictionary look up that also wants to contain a nested dictionary.
exists() function takes a character argument with the variable name:
if(exists("this.mydict")){
# you can use this.mydict here
}else{
# initialize this.mydict
# e.g. this.mydict <- "some value here"
}