How to convert an IEnumerable<IEnumerable<T>> to a IEnumerable<T> - c#

I have an IEnumerable<IEnumerable<T>> collection that I want to convert to a single dimension collection. Is it possible to achieve this with a generic extension method? Right now I'm doing this to achieve it.
List<string> filteredCombinations = new List<string>();
//For each collection in the combinated results collection
foreach (var combinatedValues in combinatedResults)
{
List<string> subCombinations = new List<string>();
//For each value in the combination collection
foreach (var value in combinatedValues)
{
if (value > 0)
{
subCombinations.Add(value.ToString());
}
}
if (subCombinations.Count > 0)
{
filteredCombinations.Add(String.Join(",",subCombinations.ToArray()));
}
}
If it's not possible to get a generic solution, how can I optimize this in an elegant fashioned way.

You can use the Enumerable.SelectMany extension method for this.
If I read your code correctly, the code for that would be:
var filteredCombinations = combinatedResults.SelectMany(o => o)
.Where(value => value > 0)
.Select(v => v.ToString());
Edit: As commented, the above code is not joining each element of the subsets to a string, as the original code does. Using the built-in methods, you can do that using:
var filteredCombinations = combinatedResults
.Where(resultSet => resultSet.Any(value => value > 0)
.Select(resultSet => String.Join(",",
resultSet.Where(value => value > 0)
.Select(v => v.ToString()).ToArray()));

Here you go:
var strings = combinedResults.Select
(
c => c.Where(i => i > 0)
.Select(i => i.ToString())
).Where(s => s.Any())
.Select(s => String.Join(",", s.ToArray());

I would personally use Enumerable.SelectMany, as suggested by driis.
However, if you wanted to implement this yourself, it would be much cleaner to do:
IEnumerable<T> MakeSingleEnumerable<T>(IEnumerable<IEnumerable<T>> combinatedResults)
{
foreach (var combinatedValues in combinatedResults) {
foreach (var value in combinatedValues)
yield return value;
}
}

You asked two different questions. The one you described in the title is already answered by drilis.
But your example code is a different problem. We can refactor it in stages. Step 1, build the subCombinations list using some Linq:
List<string> filteredCombinations = new List<string>();
//For each collection in the combinated results collection
foreach (var combinatedValues in combinatedResults)
{
var subCombinations = combinatedValues.Where(v => v > 0)
.Select(v => v.ToString())
.ToList();
if (subCombinations.Count > 0)
filteredCombinations.Add(string.Join(",",subCombinations.ToArray()));
}
Now the outer loop, leaving us with just this:
var filteredCombinations = combinatedResults
.Select(values => values.Where(v => v > 0)
.Select(v => v.ToString())
.ToArray())
.Where(a => a.Count > 0)
.Select(a => string.Join(",", a));

use linq SelectMany

Related

How do I pick out values between a duplicate value in a collection?

I have a method that returns a collection that has a duplicate value.
static List<string> GenerateItems()
{
var _items = new List<string>();
_items.Add("Tase");
_items.Add("Ray");
_items.Add("Jay");
_items.Add("Bay");
_items.Add("Tase");
_items.Add("Man");
_items.Add("Ran");
_items.Add("Ban");
return _items;
}
I want to search through that collection and find the first place that duplicate value is located and start collecting all the values from the first appearance of the duplicate value to its next appearance. I want to put this in a collection but I only want the duplicate value to appear once in that collection.
This is what I have so far but.
static void Main(string[] args)
{
string key = "Tase";
var collection = GenerateItems();
int index = collection.FindIndex(a => a == key);
var matchFound = false;
var itemsBetweenKey = new List<string>();
foreach (var item in collection)
{
if (item == key)
{
matchFound = !matchFound;
}
if (matchFound)
{
itemsBetweenKey.Add(item);
}
}
foreach (var item in itemsBetweenKey)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
There must be an easier way of doing this. Perhaps with Indexing or a LINQ query?
You can do something like that
string key = "Tase";
var collection = GenerateItems();
int indexStart = collection.FindIndex(a => a == key);
int indexEnd = collection.FindIndex(indexStart+1, a => a == key);
var result = collection.GetRange(indexStart, indexEnd-indexStart);
You can use linq select and group by to find the first index and last index of all duplicates (Keep in mind if something is in the list more then 2 times it would ignore the middle occurences.
But I personally think the linq for this seems overcomplicated. I would stick with simple for loops and if statements (Just turn it into a method so it reads better)
Here is a solution with Linq to get all duplicate and all values between those duplicates including itself once as you mentioned.
var collection = GenerateItems();
var Duplicates = collection.Select((x,index) => new { index, value = x })
.GroupBy(x => x.value)//group by the strings
.Where(x => x.Count() > 1)//only take duplicates
.Select(x=>new {
Value = x.Key,
FirstIndex = x.Min(y=> y.index),//take first occurenc
LastIndex = x.Max(y => y.index)//take last occurence
}).ToList();
var resultlist = new List<List<string>>();
foreach (var duplicaterange in Duplicates)
resultlist .Add(collection.GetRange(duplicaterange.FirstIndex, duplicaterange.LastIndex - duplicaterange.FirstIndex));
Try this function
public List<string> PickOut(List<string> collection, string key)
{
var index = 0;
foreach (var item in collection)
{
if (item == key)
{
return collection.Skip(index).TakeWhile(x=> x != key).ToList();
}
index++;
};
return null;
}
First finding the duplicate key then find the second occurrence of the item and then take result.
var firstduplicate = collection.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key).First();
var indices = collection.Select((b, i) => b == firstduplicate ? i : -1).Where(i => i != -1).Skip(1).FirstOrDefault();
if (indices>0)
{
var result = collection.Take(indices).ToList();
}

Get Duplicates in List and Edit Item

I need to get any items in a list that already exist and edit BOTH items to include a duplicate property.
I've attempted to get duplicates by doing:
var duplicates = LIST.Select((t, i) => new { Index = i, Text = t }).GroupBy(g => g.Text.PROPERTYTOSEARCGBY).Where(g => g.Count() > 1);
However this returns me the property that is duplicated when however I need to get index of both of the records so that I can edit them using:
LIST[index1].FlaggedData = true;
LIST[index2].FlaggedData = true;
etc...
How can I get the index of BOTH duplicate or multiple records?
The variable duplicates is an IGrouping that can be iterated. Each element in the enumeration will be an anonymous type that you defined to have 2 properties: Index and Text.
foreach (var grouping in duplicates)
{
// This will contain the value that was grouped by:
// - grouping.Key
foreach (var pair in grouping)
{
// These properties are available
// - pair.Index
// - pair.Text
// set the FlaggedData property
pair.Text.FlaggedData = true;
}
}
You already have the correct LINQ query to do what you want. Seems like your code to update the Duplicate flag is incorrect. You should use some code like below to update the Dupe flag
foreach (var group in duplicates) {
foreach (var item in group) {
LIST[item.Index].FlaggedData = true;
}
}
or some more concise code like
foreach (var item in duplicates.SelectMany(item => item))
item.Text.FlaggedData = true;
Use SelectMany
duplicates = LIST.Select((t, i) => new { Index = i, Text = t })
.GroupBy(g => g.Text.PROPERTYTOSEARCGBY)
.Where(g => g.Count() > 1)
.SelectMany(g => g, (g, x) => x.Index);
You don't need the index.
It's enough to group as you did and select the item themselves.
var duplicates = LIST.GroupBy(t => t.PROPERTYTOSEARCGBY)
.Where(g => g.Count() > 1)
.SelectMany(t=>t);
foreach (var item in duplicates)
{
item.FlaggedData = true;
}
Or even shorter:
LIST.GroupBy(t => t.PROPERTYTOSEARCGBY)
.Where(g => g.Count() > 1)
.SelectMany(t=>t)
.ToList()
.ForEach(t=>t.FlaggedData = true);

LINQ - Select min count

I have a list of strings which contain X in them. I want to select list(s) with the minimum count of X in them. For example:
CountMin("AXBXX", "AAX") will return AAX.
How can I write this qith LINQ in a concise way ?
public static string CountMin(IList<string> inputList)
{
if (inputList == null || !inputList.Any()) return null;
var result = inputList.Select(s => new
{
Item = s,
Count => s.Count(ch => ch == 'X')
})
.OrderBy(item => item.Count).First().Item;
}
Snippet assumes that all elements on list are different to null. If you need it, it could be easily improved.
You can also omit temporary class:
inputList.OrderBy(s => s.Count(c => c == 'X')).First();
string[] list = {"AXBXX", "AAX", "AXX"};
string result = (from word in list
select new { word, wordLen = (word.Length - (word.Replace("X", "")).Length) })
.OrderBy(x => x.wordLen).First().word;
MessageBox.Show(result);
Here's an answer that will get you all of the minimum X strings from the list.
var listOfStrings = new List<string>()
{
"AXB",
"ABXXC",
"ABX",
};
var minimumXs =
listOfStrings
.GroupBy(x => x.Count(y => y == 'X'))
.OrderBy(x => x.Key)
.Take(1)
.SelectMany(x => x);
That gives me:
AXB
ABX

Loop generates a lot of lines?

I cannot wrap my head around why this code generates a lot of null-lines:
int nr = 0;
foreach (var item in lists.Select(x => x.match_id))
{
foreach (var match in lists)
{
Console.Write(match.nickname
.Where(x => lists[nr].match_id == match.match_id)
.Select(z => match.nickname)
.FirstOrDefault());
}
nr++;
}
lists is an array of objects. The output:
Below all of the nulls are the next records, and then more nulls etc. Why?
To not output the null, you need to not call Console.Write if the value is null.
Try something like this:
int nr = 0;
foreach (var item in lists.Select(x => x.match_id))
{
foreach (var match in lists)
{
var n = match.nickname
.Where(x => lists[nr].match_id == match.match_id)
.Select(z => match.nickname)
.FirstOrDefault();
if (n != null)
{
Console.Write(n);
}
}
nr++;
}
You are looping twice through the same Array. I think you want to do:
for(int i = 0; i < lists.length; i++){
Console.Write(item.nickname
.Where(x=> lists[i].match_id === item.match_id)
.Select( z=> item.nickname)
.FirstOrDefault());
}
My 2 cents after a quick look..

Find multiple index in array

Say I have an array like this
string [] fruits = {"watermelon","apple","apple","kiwi","pear","banana"};
Is there an built in function that allows me to query all the index of "apple" ?
For example,
fruits.FindAllIndex("apple");
will return an array of 1 and 2
If there is not, how should I implement it?
Thanks!
LINQ version:
var indexes = fruits.Select((value, index) => new { value, index })
.Where(x => x.value == "apple")
.Select(x => x.index)
.ToList();
Non-LINQ version, using Array<T>.IndexOf() static method:
var indexes = new List<int>();
var lastIndex = 0;
while ((lastIndex = Array.IndexOf(fruits, "apple", lastIndex)) != -1)
{
indexes.Add(lastIndex);
lastIndex++;
}
One way would be to write like this:
var indices = fruits
.Select ((f, i) => new {f, i})
.Where (x => x.f == "apple")
.Select (x => x.i);
Or the traditional way:
var indices = new List<int>();
for (int i = 0; i < fruits.Length; i++)
if(fruits[i] == "apple")
indices.Add(i);
Pretty easy with an extension method.
var fruits = new[] { "watermelon","apple","apple","kiwi","pear","banana" };
var indexes = fruits.FindAllIndexes("apple");
public static class Extensions
{
public static int[] FindAllIndexes(this string[] array, string search) => array
.Select((x, i) => (x, i))
.Where(value => value.x == search)
.Select(value => value.i)
.ToArray();
}

Categories

Resources