Convert a sequence of numbers into a zero based order? - c#

Suppose I have:
var correctOrder = new[] {2, 1, 0};
var actualPositionsFound = new[] {63,62,61];
How can I easily convert actualPositionsFound to a zero based sequence?
So if I had:
var actualPositionsFound = new[] {100,50,200];
I would like to end up with :
var result = new[] {1,0,2};
Update: In an attempt to make this clearer to avoid closing, what I believe is being asked for is to translate a list of numbers into another list of numbers representing the ascending order of the other list like a sort map, 0-based.
So { 16, 19, 2, 4 } would create a map { 2, 3, 0, 1 }, being 0-based.

If there are no duplicates:
var actualPositionsFound = new[] { 100, 50, 200 };
var indices = actualPositionsFound.OrderBy(n => n)
.Select((n, i) => new { n, i })
.ToDictionary(o => o.n, o => o.i);
var result = actualPositionsFound.Select(n => indices[n]).ToList();

Is it that you are looking for?
actualPositionsFound.Select((elem, idx) => new { elem, idx })
.OrderBy(wrap => wrap.elem)
.Select((wrap, idx) => new { wrap.idx, newIdx = idx })
.OrderBy(wrap => wrap.idx)
.Select(wrap => wrap.newIdx)
.ToArray();

actualPositionsFound
.OrderBy(x => x).ToList()
.Select(x => Array.IndexOf(actualPositionsFound,x)).ToArray();
This won't handle duplicates.

Related

Check if a set exactly includes a subset using Linq taking into account duplicates

var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
subset.All(s => superset.Contains(s))
This code would return true, because 9 is included in the superset,but only once, I want an implementation that would take into account the duplicates, so it would return false
My thought was that you could group both sets by count, then test that the super group list contained every key from the sub group list and, in each case, the super count was greater than or equal to the corresponding subcount. I think that I've achieved that with the following:
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subGroups = subset.GroupBy(n => n).ToArray();
var superGroups = superset.GroupBy(n => n).ToArray();
var basicResult = subset.All(n => superset.Contains(n));
var advancedResult = subGroups.All(subg => superGroups.Any(supg => subg.Key == supg.Key && subg.Count() <= supg.Count()));
Console.WriteLine(basicResult);
Console.WriteLine(advancedResult);
I did a few extra tests and it seemed to work but you can test some additional data sets to be sure.
Here is another solution :
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subsetGroup = subset.GroupBy(x => x).Select(x => new { key = x.Key, count = x.Count() });
var supersetDict = superset.GroupBy(x => x).ToDictionary(x => x.Key, y => y.Count());
Boolean results = subsetGroup.All(x => supersetDict[x.key] >= x.count);
This works for me:
var subsetLookup = subset.ToLookup(x => x);
var supersetLookup = superset.ToLookup(x => x);
bool flag =
subsetLookup
.All(x => supersetLookup[x.Key].Count() >= subsetLookup[x.Key].Count());
That's not how sets and set operations work. Sets cannot contain duplicates.
You should treat the two arrays not as sets, but as (unordered) sequences. A possible algorithm would be: make a list from the sequence superset, then remove one by one each element of the sequence subset from the list until you are unable to find such an element in the list.
bool IsSubList(IEnumerable<int> sub, IEnumerable<int> super)
{
var list = super.ToList();
foreach (var item in sub)
{
if (!list.Remove(item))
return false; // not found in list, so sub is not a "sub-list" of super
}
return true; // all elements of sub were found in super
}
var subset = new[] { 9, 3 };
var superset = new[] { 9, 10, 5, 3,1, 3, 3 };
var isSubSet = IsSubList(subset, superset);

How to get specific columnar maximums in List of arrays?

I have a list "XYZarr" filled with array of integers as shown below.
int[] intarr1 = new int[3]{ 1, 4, 15};
int[] intarr2 = new int[3] { 10, 5, 8};
int[] intarr3 = new int[3] { 7, 8, 12 };
int[] intarr4 = new int[3] { 7, 8, 9 };
List<int[]> XYZarr = new List<int[]>() { intarr1, intarr2, intarr3, intarr4 };
I want to get only three indexes from the list where int[2] is maximum(i.e third entry). so the resulting indexes should be 0,2,3 of the list "XYZarr"
I can do it by looping through the list , but i want to use LinQ for this. I am not able to phrase the question properly, but..i hope you get my point
How about this:
var result = Enumerable.Range(0, XYZarr.Count)
.OrderByDescending(i => XYZarr[i][2])
.Take(3)
.ToList();
I am assuming that "int[2] is maximum" means that the 3rd element in the array is the maximum element in that array.
Here is how:
var result =
XYZarr
.Select((list,index)
=> new {list,index})
.Where(x => x.list.Max() == x.list[2])
.Select(x => x.index)
.ToList();
If XYZarr contains more than 3 arrays and you just want to get 3 results, then use the Take method like this:
var result =
XYZarr
.Select((list,index)
=> new {list,index})
.Where(x => x.list.Max() == x.list[2])
.Select(x => x.index)
.Take(3)
.ToList();

How to return duplicate values from an array?

Is there a way that I could return duplicate values from an array in C#? also im looking to write a small algorithm that would return the most number of duplicate values in an array. for example
[1, 2,2,2 3,3] I need to return the duplicate values with the most number of occurrences and the number of occurrences as well.
I think I saw some post which said that It could be done using Linq but I have no clue what Linq is
Any help would be much appreciated.
Try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3 };
IGrouping<int, int> mostOccurrences = data
.GroupBy(value => value)
.OrderByDescending(group => group.Count())
.First();
Console.WriteLine("Value {0} occurred {1} time(s).", mostOccurrences.Key, mostOccurrences.Count());
Note that if multiple values occur the same number of times (such as if you added another 3 to that list), the above code will only list one of them. To handle that situation, try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3, 3 };
var occurrenceInfos = data
.GroupBy(value => value)
.Select(group =>
new {
Count = group.Count(),
Value = group.Key
}
);
int maxOccurrenceCount = occurrenceInfos.Max(info => info.Count);
IEnumerable<int> maxOccurrenceValues = occurrenceInfos
.Where(info => info.Count == maxOccurrenceCount)
.Select(info => info.Value);
foreach (int value in maxOccurrenceValues)
Console.WriteLine("Value {0} occurred {1} time(s).", value, maxOccurrenceCount);
Here's my take on this:
var data = new[] { 1, 2, 2, 2, 3, 3, };
var occurences =
data
.ToLookup(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var mostOccurences =
occurences
.OrderByDescending(x => x.Value)
.First();
These will give you the following results:

name of assoc-diff in C#

What do you call this method, (is it available in .net?)
var list1 = new List<int>() { 1, 2, 2, 3, 4 };
var list2 = new List<int>() { 1, 2, 3};
var results = list1.diff(list2);
results:
{ 2, 4 }
The closest thing built in is the Except LINQ operator.
Produces the set difference of two sequences.
Though with your example it will result in:
{ 4 }
I don't believe there is a direct analogue to what you want.
You actually need a multiset implementation. Although there is no multiset out of the box in BCL, there are some ideas here and in the linked question.
Or you can actually implement one by yourself, it's not so complicated:
class Multiset<K> // maybe implement IEnumerable?
{
Dictionary<K, int> arities = new Dictionary<K, int>();
...
Multiset<K> Except(Multiset<K> other)
{
foreach (var k in arities.keys)
{
int arity = arities[k];
if (other.Contains(k))
arity -= other.Arity(k);
if (arity > 0)
result.Add(k, arity);
}
return result;
}
}
This exactly return what you want, You can refactor it in a Extension Method:
var results = list1.GroupBy(p => p).Select(p => new { item = p.Key, count = p.Count() })
.Concat(list2.GroupBy(p => p).Select(p => new { item = p.Key, count = -p.Count() }))
.GroupBy(p => p.item).Select(p => new { item = p.Key, count = p.Sum(q => q.count) })
.Where(p => p.count > 0)
.SelectMany(p => Enumerable.Repeat(p.item, p.count));
Like this: (see oded's post for a linq to msdn)
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);

How to group items by index? C# LINQ

Suppose I have
var input = new int[] { 0, 1, 2, 3, 4, 5 };
How do I get them grouped into pairs?
var output = new int[][] { new int[] { 0, 1 }, new int[] { 2, 3 }, new int[] { 4, 5 } };
Preferably using LINQ
input
.Select((value, index) => new { PairNum = index / 2, value })
.GroupBy(pair => pair.PairNum)
.Select(grp => grp.Select(g => g.value).ToArray())
.ToArray()
Probably not applicable to you, but you could use the new Zip method in C# 4.0
var input = new int[] { 0, 1, 2, 3, 4, 5 };
IEnumerable evens = input.Where((element, index) => index % 2 == 0);
IEnumerable odds = input.Where((element, index) => index % 2 == 1);
var results = evens.Zip(odds, (e, o) => new[] { e, o }).ToArray();
var indexedNumbers = input.Select((number, index) => new { Index = index, Number = number });
var pairs =
from indexedNumber in indexedNumbers
group indexedNumber by indexedNumber.Index / 2 into indexedNumberPair
select indexedNumberPair.Select(indexedNumber => indexedNumber.Number);
var arrays = pairs.Select(pair => pair.ToArray()).ToArray();
Using ToLookup method:
input
.Select((number, index) => new { index , number})
.ToLookup(_ => _.index / 2, _ => _.number)
.Select(_ => _.ToArray())
.ToArray();
Using Zip method:
input
.Zip(input.Skip(1), (_, __) => new[] {_, __})
.Where((_, index) => index % 2 == 0)
.ToArray();

Categories