Ruby .each with removal of items in collection - c#

I am currently working with an app that allows for runtime addition and removal of items in a drop down list via a rub script. The Ruby looks like this
SAFE = ;
return control if control.CyclesCount == 0;
control.Items.each{|item| control.Items.Remove(item) if item.Value.index('|').nil?};
return control;
control is custom user control and its Items is a ListItemCollction. I am running via unit tests to get my Ruby code correct and running into some trouble. The ListItemColletion I am passing in looks like this ..
var lic = new ListItemCOllection {
new ListItem {Text = "Item2", Value = "8"},
new ListItem {Text = "Item1", Value = "1"},
new ListItem {Text = "Item3", Value = "3|1"},
new ListItem {Text = "Item4", Value = "4"},
new ListItem {Text = "Item5", Value = "5|2"},
new ListItem {Text = "Item6", Value = "6"}
}
Instead of leaving the 2 items with the pipe in them, this code always seems to leave 3 items in the items collection. The 3 depend on the order that I put the items in (while in this order, Item1, Item3, Item5 get left)which leads me to believe that its the remove that is messed up. I have also tried to take a copy of the collection, loop through it, removing from the original so that I was not removing from the collection I was iterating through. I am a relatve noob to Ruby so go easy on me ... but I could use some advice.
Thanks

It is not advisable to change an array while iterating over it. There are some Iterators whose purpose is to change the array.
a= [1,2,3]
b= [1,2,3]
a.delete_if { |x| x == 2 } # => [1,3]
b.reject! { |x| x == 2 } # => [1,3]
a # => [1,3]
b # => [1,3]
Array#delete_if deletes elements of an array. There is only a minor difference to Array#reject
a= [1,2,3]
b= [1,2,3]
a.delete_if { |x| false } # => [1,3]
b.reject! { |x| false } # => nil
a # => [1,2,3]
b # => [1,2,3]
Array#delete_if always returns the remaining array. Array#reject! returns nil instead in case the array remains unchanged.
Some more modifiieng iterators which, do not change the original array:
a= [1,2,3]
a.reject { |x| x == 2 } # => [1,3]
a # => [1,2,3]
Array#reject returns an array without the rejected elements, but does not modify the original array.
a= [1,2,3]
a.select { |x| x != 2 } # => [1,2,3]
a # => [1,3]
Array#select returns an array of only the selected elements, but does not modify the original array.

Modifying a collection while you are iterating through it is never a good idea. If you do that, all hell breaks loose. (Preferably, it would raise some kind of exception, but that's life ...)
However, that's not actually the biggest problem in your code. The biggest problem is that you have commited the mortal sin of Ruby: not knowing Enumerable. (Don't worry: everybody commits that sin. All the time.)
If you want to reject all elements from a collection that satisfy a condition, there's a method for that, and it's called exactly what you would expect: Enumerable#reject!.
So, let's clean this up, shall we?
SAFE = ;
What's that semicolon doing there? It seems you got your C# and Ruby mixed up :-)
(Oh, and also, that line doesn't do anything useful anyway, does it?)
return control if control.CyclesCount == 0;
Again, useless semicolon.
control.Items.each{|item| control.Items.Remove(item) if item.Value.index('|').nil?};
This is where it gets interesting:
control.Items.reject! {|item| item.Value.include?('|') }
Much better, isn't it?
return control;
I personally like to reserve the return keyword for "pure" methods (i.e. methods that don't have side-effects), so I wouldn't use one here since the code modifies control.Items but that is a style choice. Putting it all together, this is how I would write it:
return control if control.cycles_count == 0
control.items.reject! {|item| item.value.include?('|') }
control
Note: I don't have a working install of IronRuby right now, so I am making a few assumptions that I can unfortunately not test:
method name transliteration (CyclesCount -> cycles_count) works,
Value is some kind of String or collection and
ListItemCollection mixes in Enumerable
The latter should be the case if ListItemCollection implements IEnumerable (otherwise I would consider that a bug in IronRuby). If ListItemCollection does not implement IEnumerable (which I would probably consider a bug in ListItemCollection), that is still easily fixed:
class ListItemCollection; include Enumerable end
[BTW: I would also introduce a cycles? method (or a bool HasCycles property on the .NET side), so that you can get rid of the cycle_count == 0 test.]

If you just want to remove items from an array based on a condition, you should use Array#reject!:
control.Items.reject! {|item| item.Value.index('|').nil? };
In order to properly debug this, however, we need to know what control.Items looks like on the Ruby end.

Related

Replace a string placeholder with consecutive elements from a list in c#

i know similar questions have been asked, but I couldn't find anything specifically fitting my need and I am supremely ignorant about Regex.
I have sentences of varying length like this one:
Provides a +$modifier% bonus to Maximum Quality and a +$modifier% chance for Special Traits when developing a Recipe.
so that $modifier is my placeholder for all of them. I have a list of floats that I will then replace accordingly to the order.
In this case I have a List values {5,0.5}. The replaced string should end up as
Provides a +5% bonus to Maximum Quality and a +0.5% chance for Special Traits when developing a Recipe.
I would like to avoid string.Replace as texts might get longer and i wouldn't like to loop multiple time over it. Could anyone suggest a good approach to do it?
Cheers and thanks
H
The method Regex.Replace has an overload where you can specify a callback method to provide the value to use for each replacement.
private string ReplaceWithList(string source, string placeHolder, IEnumerable<object> list)
{
// Escape placeholder so that it is a valid regular expression
placeHolder = Regex.Escape(placeHolder);
// Get enumerator for list
var enumerator = list.GetEnumerator();
// Use Regex engine to replace all occurences of placeholder
// with next entry from enumerator
string result = Regex.Replace(source, placeHolder, (m) =>
{
enumerator.MoveNext();
return enumerator.Current?.ToString();
});
return result;
}
Use like that:
string s = "Provides a +$modifier% bonus to Maximum Quality and a +$modifier% chance for Special Traits when developing a Recipe.";
List<object> list = new List<object> { 5, 0.5 };
s = ReplaceWithList(s, "$modifier", list);
Note that you need to add sensible error handling.
As strings are immutable in the CLR there's probably no sensible way around splitting your string and putting it back together in some way.
One would be to split at your desired marker string, insert your replacement values and afterwards concatenate your parts again:
var s = "Provides a +$modifier % bonus to Maximum Quality and a +$modifier % chance for Special Traits when developing a Recipe.";
var v = new List<float> { 5.0f, 0.5f };
var result = string.Concat(s.Split("$modifier").Select((s, i) => $"{s}{(i < v.Count ? v[i] : string.Empty)}"));

merging k sorted lists does not work with negative values C#

The question for the problem is:
You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.
Merge all the linked-lists into one sorted linked-list and return it.
A successful working example with inputs and outputs is:
Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6
The issue with my code is that it is not working for negative values however it works fine for positive values.
eg input -> [[2],[1],[-1]] output->[1,2]
public class Solution {
public ListNode MergeKLists(ListNode[] lists) {
if (lists.Length == 0) return null;
var newlist = new ListNode();
var result = newlist;
for(int i=0; i<lists.Length; i++)
{
if(lists[i] !=null)
newlist = MergeTwoLists(lists[i], newlist);
}
return result.next;
}
private ListNode MergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null)
return l2;
if (l2 == null)
return l1;
if (l1.val <= l2.val)
{
l1.next = MergeTwoLists(l1.next, l2);
return l1;
}
else
{
l2.next = MergeTwoLists(l1, l2.next);
return l2;
}
}
}
Fundamentally, the problem with your code is that you initialize the algorithm with a non-empty list. I.e. you set newlist to a single ListNode object. You haven't provided a complete code example, but presumably this is a class with at least two members, val and next. In C#, both of these members will initially have their default values, which means you start out with the list [0], before you've even started merging anything.
Note also that while you are modifying the newlist variable in the merging loop, what you return is the result.next variable. This appears to be an attempt to skip the erroneously include 0 value that you put in the newlist list in the first place. But in your negative-valued example, it causes you to skip both the 0 value, and the -1 value that the merge correctly placed before it.
In your example [[2],[1],[-1]], this means that when the loop for merging is done, you have newlist referring to the list [-1, 0, 1, 2]. But result points to the second element of that list (the original 0-valued node), giving you [0, 1, 2]. Then what you return is the next node of that, which produces [1,2].
The fact is, the MergeTwoLists() method you show already handles empty, i.e. null-valued, lists. It's not clear what motivated you to initialize your algorithm with a non-empty list, nor what motivated you to keep a second variable to reference that same node. Your problem probably would've been easier for you to notice if you hadn't done the latter, and of course, the whole bug is caused by the former.
You should just remove both aspects from the code you already have. Initialize newlist to null instead of creating a new node for it, and get rid of the result variable altogether:
public ListNode MergeKLists(ListNode[] lists) {
var newlist = null;
for (int i = 0; i < lists.Length; i++)
{
if (lists[i] != null) {
newlist = MergeTwoLists(lists[i], newlist);
}
}
return newlist;
}
Note: you also don't need the check for lists.Length == 0. The loop will be skipped if lists.Length is 0, and with the fixed version just returning null as the empty list in that case, it works just as well without it.
On a more general note: most of the time, bugs are fixed by changing code, or even removing it. If you get into the habit of fixing bugs by adding code, more often than not you just wind up with a new bug added to the one you already had.
I will admit, that rule is not a hard-and-fast, 100% reliable one. But it's served me very well over the years.

Why isn't assign on a foreach iteration variable in Array.ForEach an error?

Given an array of int numbers like:
int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };
If we want to increment every number by 1, the best choice would be:
for(int i = 0; i < arr.Length; i++)
{
arr[i]++;
}
If we try to do it using foreach
foreach(int n in arr)
{
n++;
}
as expected, we meet the error:
Cannot assign to 'n' because it is a 'foreach iteration variable'
Why if we use this approach:
Array.ForEach(arr, (n) => {
n++;
});
which is equal to the foreach above, visual studio and compiler aren't going to tell us anything, the code is going to compile and just not producing any result in runtime, neither throw an exception?
foreach(int n in arr)
{
n++;
}
This is a language construct, the compiler knows exactly what a foreach-loop is supposed to do and what nis. It can therefore prevent you from changing the iteration variable n.
Array.ForEach(arr, (n) => {
n++;
});
This is a regular function call passing in a lambda. It is perfectly valid to modify local variables in a function (or lambda), so changing n is okay. While the compiler could warn you that the increment has no effect as it's never been used afterwards, it's valid code, and just because the function is called ForEach and actually does something similar to the foreach-loop doesn't change the fact that this is a regular function and a regular lambda.
As pointed out by #tkausl, n with ForEach is a local variable. Therefore:
static void Main()
{
int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(" ",arr));
Array.ForEach(arr, (n) => {
n++;
});
Console.WriteLine(string.Join(" ",arr));
}
will output:
0 1 2 3 4 5
0 1 2 3 4 5
Meaning you don't change the values of arr.
Array.ForEach is not identical to a foreach-loop. It´s an extension-method which will iterate a collection and performs an action on every of its elements.
Array.ForEach(arr, (n) => {
n++;
});
however won´t modify the actuzal collection, it will just re-assign a new value to n which has no relation to the underlying value in the array, because it´s a value-type which is **copied* to the anonymous method. So whatever you do with the param in your anonymous method isn´t reflected to the ForEach-method and thus has no effect in your array. This is why you can do this.
But even if you had an array of reference-types that would work, because you simply re-assign a new instance to the provided parameter, which again has no effect to the underlying array.
Take a look at this simplified example:
MyClass
{
void ForEach(Action<Item> a)
{
foreach(var e in myList)
Action(e);
}
}
In your case the action looks like this:
x => x++
which simply assigns a new value to x. As x however is passed by value, this won´t have any effect to the calling method and thus to myList.
Both are two different things.
First we need to be clear what we need. If the requirement is to mutate the existing values then you can use for loop as modifying the values while enumerating the collection shouldn't be done that' why you face error for the first foreach loop.
So one approach could be if mutating is the intention:
for(int i=0; i< arr.Length; i++)
{
arr[i] = arr[i] +1;
}
Secondly, If the intention is to get a new collection with the updated values then consider using linq Select method which will return a new collection of int.
var incrementedArray = arr.Select( x=> (x+1));
EDIT:
the key difference is in the first example we are modifying the values of colelction while enumerating it while in lambda syntax foreach a delegate is used which get input as local variable.
The foreach statement executes a statement or a block of statements for each element in an instance of the type that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface. You cannot modify iterated value because you are using System.Collections.IEnumberable or System.COllections.Generic.IEnumberable<T> interfaces which support deferred execution.
If you want to modify value you can also use
foreach(ref int n in arr)
{
n++;
}
Updated
The Array.Foreach is a method that performs specified action on each element of the specified array. This function support immediate execution behavior and can be applied to only data that holds in memory. The Array.Foreach method take an array and used For loop to iterate through collection.
foreach and Array.Foreach both looks same but are different in their working.

c# - BinarySearch StringList with wildcard

I have a sorted StringList and wanted to replace
foreach (string line3 in CardBase.cardList)
if (line3.ToLower().IndexOf((cardName + Config.EditionShortToLong(edition)).ToLower()) >= 0)
{
return true;
}
with a binarySearch, since the cardList ist rather large(~18k) and this search takes up around 80% of the time.
So I found the List.BinarySearch-Methode, but my problem is that the lines in the cardList look like this:
Brindle_Boar_(Magic_2012).c1p247924.prod
But I have no way to generate the c1p... , which is a problem cause the List.BinarySearch only finds exact matches.
How do I modify List.BinarySearch so that it finds a match if only a part of the string matches?
e. g.
searching for Brindle_Boar_(Magic_2012) should return the position of Brindle_Boar_(Magic_2012).c1p247924.prod
List.BinarySearch will return the ones complement of the index of the next item larger than the request if an exact match is not found.
So, you can do it like this (assuming you'll never get an exact match):
var key = (cardName + Config.EditionShortToLong(edition)).ToLower();
var list = CardBase.cardList;
var index = ~list.BinarySearch(key);
return index != list.Count && list[index].StartsWith(key);
BinarySearch() has an overload that takes an IComparer<T> has second parameter, implement a custom comparer and return 0 when you have a match within the string - you can use the same IndexOf() method there.
Edit:
Does a binary search make sense in your scenario? How do you determine that a certain item is "less" or "greater" than another item? Right now you only provide what would constitute a match. Only if you can answer this question, binary search applies in the first place.
You can take a look at the C5 Generic Collection Library (you can install it via NuGet also).
Use the SortedArray(T) type for your collection. It provides a handful of methods that could prove useful. You can even query for ranges of items very efficiently.
var data = new SortedArray<string>();
// query for first string greater than "Brindle_Boar_(Magic_2012)" an check if it starts
// with "Brindle_Boar_(Magic_2012)"
var a = data.RangeFrom("Brindle_Boar_(Magic_2012)").FirstOrDefault();
return a.StartsWith("Brindle_Boar_(Magic_2012)");
// query for first 5 items that start with "Brindle_Boar"
var b = data.RangeFrom("string").Take(5).Where(s => s.StartsWith("Brindle_Boar"));
// query for all items that start with "Brindle_Boar" (provided only ascii chars)
var c = data.RangeFromTo("Brindle_Boar", "Brindle_Boar~").ToList()
// query for all items that start with "Brindle_Boar", iterates until first non-match
var d = data.RangeFrom("Brindle_Boar").TakeWhile(s => s.StartsWith("Brindle_Boar"));
The RageFrom... methods perform a binary search, find the first element greater than or equal to your argument, that returns an iterator from that position

A little help needed in code translation (Python to C#)

Good night everyone,
This question leaves me a little embarassed because, of couse, I know I should be able to get the answer alone. However, my knowledge about Python is just a little bit more than nothing, so I need help from someone more experienced with it than me...
The following code comes from Norvig's "Natural Language Corpus Data" chapter in a recently edited book, and it's about transforming a sentence "likethisone" into "[like, this, one]" (that means, segmenting the word correctly)...
I have ported all of the code to C# (in fact, re-wrote the program by myself) except for the function segment, which I am having a lot of trouble even trying to understand it's syntax. Can someone please help me translating it to a more readable form in C#?
Thank you very much in advance.
################ Word Segmentation (p. 223)
#memo
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
candidates = ([first]+segment(rem) for first,rem in splits(text))
return max(candidates, key=Pwords)
def splits(text, L=20):
"Return a list of all possible (first, rem) pairs, len(first)<=L."
return [(text[:i+1], text[i+1:])
for i in range(min(len(text), L))]
def Pwords(words):
"The Naive Bayes probability of a sequence of words."
return product(Pw(w) for w in words)
#### Support functions (p. 224)
def product(nums):
"Return the product of a sequence of numbers."
return reduce(operator.mul, nums, 1)
class Pdist(dict):
"A probability distribution estimated from counts in datafile."
def __init__(self, data=[], N=None, missingfn=None):
for key,count in data:
self[key] = self.get(key, 0) + int(count)
self.N = float(N or sum(self.itervalues()))
self.missingfn = missingfn or (lambda k, N: 1./N)
def __call__(self, key):
if key in self: return self[key]/self.N
else: return self.missingfn(key, self.N)
def datafile(name, sep='\t'):
"Read key,value pairs from file."
for line in file(name):
yield line.split(sep)
def avoid_long_words(key, N):
"Estimate the probability of an unknown word."
return 10./(N * 10**len(key))
N = 1024908267229 ## Number of tokens
Pw = Pdist(datafile('count_1w.txt'), N, avoid_long_words)
Let's tackle the first function first:
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
candidates = ([first]+segment(rem) for first,rem in splits(text))
return max(candidates, key=Pwords)
It takes a word and returns the most likely list of words that it could be, so its signature will be static IEnumerable<string> segment(string text). Obviously if text is an empty string, its result should be an empty list. Otherwise, it creates a recursive list comprehension defining the possible candidate lists of words and returns the maximum based on its probability.
static IEnumerable<string> segment(string text)
{
if (text == "") return new string[0]; // C# idiom for empty list of strings
var candidates = from pair in splits(text)
select new[] {pair.Item1}.Concat(segment(pair.Item2));
return candidates.OrderBy(Pwords).First();
}
Of course, now we have to translate the splits function. Its job is to return a list of all possible tuples of the beginning and end of a word. It's fairly straightforward to translate:
static IEnumerable<Tuple<string, string>> splits(string text, int L = 20)
{
return from i in Enumerable.Range(1, Math.Min(text.Length, L))
select Tuple.Create(text.Substring(0, i), text.Substring(i));
}
Next is Pwords, which just calls the product function on the result of Pw on each word in its input list:
static double Pwords(IEnumerable<string> words)
{
return product(from w in words select Pw(w));
}
And product is pretty simple:
static double product(IEnumerable<double> nums)
{
return nums.Aggregate((a, b) => a * b);
}
ADDENDUM:
Looking at the full source code, it is apparent that Norvig intends the results of the segment function to be memoized for speed. Here's a version that provides this speed-up:
static Dictionary<string, IEnumerable<string>> segmentTable =
new Dictionary<string, IEnumerable<string>>();
static IEnumerable<string> segment(string text)
{
if (text == "") return new string[0]; // C# idiom for empty list of strings
if (!segmentTable.ContainsKey(text))
{
var candidates = from pair in splits(text)
select new[] {pair.Item1}.Concat(segment(pair.Item2));
segmentTable[text] = candidates.OrderBy(Pwords).First().ToList();
}
return segmentTable[text];
}
I don't know C# at all, but I can explain how the Python code works.
#memo
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
candidates = ([first]+segment(rem) for first,rem in splits(text))
return max(candidates, key=Pwords)
The first line,
#memo
is a decorator. This causes the function, as defined in the subsequent lines, to be wrapped in another function. Decorators are commonly used to filter inputs and outputs. In this case, based on the name and the role of the function it's wrapping, I gather that this function memoizes calls to segment.
Next:
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
Declares the function proper, gives a docstring, and sets the termination condition for this function's recursion.
Next is the most complicated line, and probably the one that gave you trouble:
candidates = ([first]+segment(rem) for first,rem in splits(text))
The outer parentheses, combined with the for..in construct, create a generator expression. This is an efficient way of iterating over a sequence, in this case splits(text). Generator expressions are sort of a compact for-loop that yields values. In this case, the values become the elements of the iteration candidates. "Genexps" are similar to list comprehensions, but achieve greater memory efficiency by not retaining each value that they produce.
So for each value in the iteration returned by splits(text), a list is produced by the generator expression.
Each of the values from splits(text) is a (first, rem) pair.
Each produced list starts with the object first; this is expressed by putting first inside a list literal, i.e. [first]. Then another list is added to it; that second list is determined by a recursive call to segment. Adding lists in Python concatenates them, i.e. [1, 2] + [3, 4] gives [1, 2, 3, 4].
Finally, in
return max(candidates, key=Pwords)
the recursively-determined list iteration and a key function are passed to max. The key function is called on each value in the iteration to get the value used to determine whether or not that list has the highest value in the iteration.

Categories