How do I flatten an array of arrays? - c#

I have an array consisting of following elements:
var schools = new [] {
new object[]{ new[]{ "1","2" }, "3","4" },
new object[]{ new[]{ "5","6" }, "7","8" },
new object[]{ new[]{ "9","10","11" }, "12","13" }
};
The real object that i try to flatten is from importing data into array of arrays from CSV and then joining it on values of fields:
var q =
from c in list
join p in vocatives on c.Line[name1].ToUpper() equals p.first_name.ToUpper() into ps
from p in ps.DefaultIfEmpty()
select new object[] { c.Line, p == null ? "(No vocative)" : p.vocative, p == null ? "(No sex)" : p.sex };
I want to flatten that array of strings to get:
string[] {
new string[]{ "1","2","3","4" },
new string[]{ "5","6","7","8" },
new string[]{ "9","10","11","12","13" }
}
I already have an solution that does that in loop, its not so performance-wise, but it seems to work ok.
I've tried to use SelectMany but cannot make up a solution.
Thank you very much for feedback ;)
I've tried answer from npo:
var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray
? (object[])y : new object[] { y })
);
But CSVwriter class method accepts only explicitly typed:
IEnumerable<string[]>
So how to do it in linq, I've tried to:
List<string[]> listOflists = (List<string[]>)result;
But no go, InvalidCastException arrises, unfortunately.

In a first step, you have to normalize the data to one kind of type. Then you can iterate over them as you like. So at first create a method to flatten the values from a specific point to an arbitrary depth:
public static class Extensions
{
public static IEnumerable<object> FlattenArrays(this IEnumerable source)
{
foreach (var item in source)
{
if (item is IEnumerable inner
&& !(item is string))
{
foreach (var innerItem in inner.FlattenArrays())
{
yield return innerItem;
}
}
yield return item;
}
}
}
Now you can either iterate on the top level to get a single array of all values:
// Produces one array => ["1", "2", "3", "4", ...]
var allFlat = schools.FlattenArrays().OfType<string>().ToArray();
Or you can create individual array one depth deeper:
foreach (var item in schools)
{
// Produces an array for each top level e.g. ["5", "6", "7", "8"]
var flat = item.FlattenArrays().OfType<string>().ToArray();
}

As per the comments, because your inner array mixes elements of string[] and string, it likely won't be trivial to do this directly in Linq.
However, with the assistance of a helper function (I've called Flattener) you can branch the handling of both of the inner types manually to either return the elements in the array (if it's string[]), or to return the single element as an enumerable, if it's not. SelectMany can then be used to flatten the inner level, but the outer level seemingly you want to leave unflattened:
i.e.
var schools = new [] {
new object[]{new[]{"1","2"}, "3","4"},
new object[]{new[]{"5","6"}, "7","8"},
new object[]{new[]{"9","10","11"}, "12","13"}
};
var result = schools
.Select(s => s.SelectMany(o => Flattener(o)));
Which returns a type of IEnumerable<IEnumerable<string>>
Where the messy unpacking bit done by:
public IEnumerable<string> Flattener(object o)
{
if (o is IEnumerable<string> strings)
{
return strings;
}
if (o is string s)
{
return new[]{s};
}
return new[]{"?"};
}
Note the above uses the pattern matching capabilities of C#7.
Result screenshot courtesy of LinqPad:

If you want o do it via linq here is a sample
var schools = new[] {
new object[]{new[]{"1","2"}, "3","4"},
new object[]{new[]{"5","6"}, "7","8"},
new object[]{new[]{"9","10","11"}, "12","13"}
};
var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray ? (object[])y : new object[] { y }));

The presented solution is devoted to convert any sort of int array, regular, jagged, or nested (these last are taken from javascript and its object notation, but they can also be implemented as complex jagged array of objects in C#), into a simple mono-dimensional integers array.
To adapt your request to it, you should have only change the string type elements of your objects jagged array into int type.
Here it is the C# function:
public static int[] getFlattenedIntArray(object jaggedArray)
{
var flattenedArray = new List<int>();
var jaggedArrayType = jaggedArray.GetType();
var expectedType = typeof(int);
if (jaggedArrayType.IsArray)
{
if (expectedType.IsAssignableFrom(jaggedArrayType.GetElementType()))
{
foreach (var el in jaggedArray as int[])
{
flattenedArray.Add(el);
}
}
else
{
foreach (var el in jaggedArray as object[])
{
foreach (var retIntEl in getFlattenedIntArray(el))
{
flattenedArray.Add(retIntEl);
}
}
}
}
else if (jaggedArrayType == expectedType)
{
flattenedArray.Add((int)jaggedArray);
}
else
{
return new int[0];
}
return flattenedArray.ToArray();
}
Try it in this fiddle: https://dotnetfiddle.net/5HGX96

Related

test if string value is contained in array of strings

Hi I have a list of words which i am looking for if this word is found i want the valuye removed from my array
private static string[] WordList = { "Example No", "Name", "Client ID"};
var listToTest = new[] { "ME 0.3", "Example No.: 1243", "Name.:"};
var newList= new List<string>();
foreach (var value in listToTest)
{
if (!WordList.Any(x => x.Contains(value)))
{
newList.Add(value);
}
}
return newList.ToArray();
my test returns all the value where I only want it to return "ME 0.3"
!WordList.Any(x => x.Contains(value)) will return true if no element in WordList contains value and false if there is at least one element in WordList that contains value.
So your code will return an array of elements of listToTest, that no element in WordList contains them, but as you said you want an array of elements of listToTest that they contain no element of WordList. So in ... x.Contains(value))) replace x and value with each other:
private static string[] WordList = { "Example No", "Name", "Client ID"};
var listToTest = new[] { "ME 0.3", "Example No.: 1243", "Name.:"};
var newList= new List<string>();
foreach (var value in listToTest)
{
if (!WordList.Any(x => value.Contains(x)))
{
newList.Add(value);
}
}
return newList.ToArray();
By the way there is a neater way:
var result = listToTest.Where(x => !WordList.Any(y => x.Contains(y))).ToArray();
// Usage:
foreach (var s in result)
Console.WriteLine(s);
// Output:
// ME 0.3
Description:
Enumerable.Where Method: Filters a sequence of values based on a predicate.
Enumerable.Any Method: Determines whether any element of a sequence exists or satisfies a condition.
Enumerable.ToArray Method: Creates an array from a IEnumerable.
String.Contains(String) Method: Returns a value indicating whether a specified substring occurs within this string.
Not the most efficient, but works.
newList = listToTest.Where((x) =>
{
return WordList.Where(x2 => x.IndexOf(x2) != -1).Count() == 0;
}).ToList();

How do you iterate through a mixed List<t> and print contents to console in C#?

I have a List<object> which contains strings and even additional lists.
List<object> NewArray = new List<object>();
so basically the list contains a mixture....
As a sanity check, I want to print out the contents to the console. I start by iterating through and test to see whether the element is a list. If it isn't then it will be a string and can be printed to the console. If it is a list, I want to iterate through and print the string contents to the console but with a tab to indent it.
I have this so far:
for (int outer = 0; outer < NewArray.Count; outer++)
{
var innerList = NewArray[outer];
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
for (int inner = 0; inner < innerList.Count; inner++)
{
//print string
}
}
else
{
//print string
}
}
I didn't want to use a foreach loop as I'm not sure the order of the list would be guaranteed and will be in the future adding a increment number (which can be provided by the inner and outer variables).
The issue I am getting is an error here:
inner < innerList.Count
which is:
Operator '<' cannot be applied to operands of type 'int' and 'method group'
What do I need to do to overcome this? I'm not sure it is the most efficient way of achieving the end result but....
static void Main()
{
var randomCrap = new List<Object>
{
1, "two",
new List<object> { 3, 4 },
5, 6,
new List<object> {
new List<object> { 7, 8, "nine" },
},
};
randomCrap.PrintAll();
}
Output:
1
two
3
4
5
6
7
8
nine
Using this:
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll())
{
Console.WriteLine(x);
}
}
public static IEnumerable<Object> SelectAll(this object o)
{
// Thank you, eocron
if (o is String)
{
yield return o;
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll())
yield return child2;
}
}
else
{
yield return o;
}
}
}
If you know your object is a List<> of some generic type, you could always cast to IList and loop through it that way.
To paraphrase your code:
if (innerList.GetType().IsGenericType && innerList.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
var list = (IList)innerList;
for (int inner = 0; inner < list.Count; inner++)
{
Console.WriteLine(list[inner].ToString());
//print string
}
}
But in reality, you should be doing what itsme86 said, and making strong types with an overriden ToString() or Display() method.
Here is more simplistic way of doing what you want:
public static void DeepPrint(object obj, int recursionLevel)
{
if(obj == null)
{
//print null
return;
}
var str = obj as string;
if(str != null)
{
//print str
return;
}
var enumer = obj as IEnumerable;
if(enumer != null)
{
foreach(var e in enumer)
{
DeepPrint(e, recursionLevel+1);
}
return;
}
//print obj.ToString();
}
Then call it like this on whatever you desire:
DeepPrint(myObjectOrList, 0);
PS
For those who say about "random crap" - embrace the string.Format(...), embrace serialization in general, embrace WCF and dynamic, and etc. There is many random things in this world, what doesn't really need strong typing. In fact it will just become "crap" if you provide strong typing to some common used functions.
One way you could do it is to check if the object implements ICollection, and if so, iterate over the contents. I created a recursive method to handle cases where a collection contained other collections, which includes an indentAmount argument, so that nested collections are indented by a tab each time they're encountered:
public static void PrintItem(object item, int indentAmount = 0)
{
var indent = new string('\t', indentAmount);
if (item == null) Console.WriteLine($"{indent}<null>");
if (item is ICollection)
{
var innerItems = item as IEnumerable;
Console.WriteLine($"{indent}Collection type encountered:");
indentAmount++;
foreach (var innerItem in innerItems)
{
PrintItem(innerItem, indentAmount);
}
}
else
{
Console.WriteLine($"{indent}{item}");
}
}
Usage
private static void Main()
{
var items = new List<object>
{
"first",
2,
new List<string> {"listFirst", "listSecond"},
new[] {"arrayFirst", "arraySecond"},
new ArrayList {"arrayListFirst", "arrayListSecond"},
"third",
new List<List<string>>
{
new List<string> {"nestedList1First", "nestedList1Second"},
new List<string> {"nestedList2First", "nestedList2Second"}
},
4f,
new object[] {5d, "six", new List<object>{"seven", 8} },
9,
"ten"
};
PrintItem(items);
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output

Use Linq to break a list by special values?

I'm trying to use Linq to convert IEnumerable<int> to IEnumerable<List<int>> - the input stream will be separated by special value 0.
IEnumerable<List<int>> Parse(IEnumerable<int> l)
{
l.Select(x => {
.....; //?
return new List<int>();
});
}
var l = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
Parse(l) // returns {{1,3,5}, {3, 4}, {1,4}}
How to implement it using Linq instead of imperative looping?
Or is Linq not good for this requirement because the logic depends on the order of the input stream?
Simple loop would be good option.
Alternatives:
Enumerable.Aggregate and start new list on 0
Write own extension similar to Create batches in linq or Use LINQ to group a sequence of numbers with no gaps
Aggregate sample
var result = list.Aggregate(new List<List<int>>(),
(sum,current) => {
if(current == 0)
sum.Add(new List<int>());
else
sum.Last().Add(current);
return sum;
});
Note: this is only sample of the approach working for given very friendly input like {0,1,2,0,3,4}.
One can even make aggregation into immutable lists but that will look insane with basic .Net types.
Here's an answer that lazily enumerates the source enumerable, but eagerly enumerates the contents of each returned list between zeroes. It properly throws upon null input or upon being given a list that does not start with a zero (though allowing an empty list through--that's really an implementation detail you have to decide on). It does not return an extra and empty list at the end like at least one other answer's possible suggestions does.
public static IEnumerable<List<int>> Parse(this IEnumerable<int> source, int splitValue = 0) {
if (source == null) {
throw new ArgumentNullException(nameof (source));
}
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext()) {
return Enumerable.Empty<List<int>>();
}
if (enumerator.Current != splitValue) {
throw new ArgumentException(nameof (source), $"Source enumerable must begin with a {splitValue}.");
}
return ParseImpl(enumerator, splitValue);
}
}
private static IEnumerable<List<int>> ParseImpl(IEnumerator<int> enumerator, int splitValue) {
var list = new List<int>();
while (enumerator.MoveNext()) {
if (enumerator.Current == splitValue) {
yield return list;
list = new List<int>();
}
else {
list.Add(enumerator.Current);
}
}
if (list.Any()) {
yield return list;
}
}
This could easily be adapted to be generic instead of int, just change Parse to Parse<T>, change int to T everywhere, and use a.Equals(b) or !a.Equals(b) instead of a == b or a != b.
You could create an extension method like this:
public static IEnumerable<IEnumerable<T>> SplitBy<T>(this IEnumerable<T> source, T value)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { };
//In case the source doesn't start with 0
if (!e.Current.Equals(value))
{
list.Add(e.Current);
}
while (e.MoveNext())
{
if ( !e.Current.Equals(value))
{
list.Add(e.Current);
}
else
{
yield return list;
list = new List<T> { };
}
}
//In case the source doesn't end with 0
if (list.Count>0)
{
yield return list;
}
}
}
}
Then, you can do the following:
var l = new List<int> { 0, 1, 3, 5, 0, 3, 4, 0, 1, 4, 0 };
var result = l.SplitBy(0);
You could use GroupBy with a counter.
var list = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
int counter = 0;
var result = list.GroupBy(x => x==0 ? counter++ : counter)
.Select(g => g.TakeWhile(x => x!=0).ToList())
.Where(l => l.Any());
Edited to fix possibility of zeroes within numbers
Here is a semi-LINQ solution:
var l = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
string
.Join(",", l.Select(x => x == 0 ? "|" : x.ToString()))
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
This is probably not preferable to using a loop due to performance and other reasons, but it should work.

How to check whether each item of an array exist in another array in mvc 4?

I have two string arrays for example selPhoto["419","418"] and preview_photo["418"].I need to check whether each element in selPhoto present in
preview_photo or not in mvc4.
How about this:
var y = new[] { "419", "418" };
var x = new[] { "418" };
Check for intersection
x.Intersect(y).Contains("418");
You can try to check if one array is a subset of another:
bool isSubset = !array2.Except(array1).Any();
So it would be like
bool isSubset = !preview_photo.Except(selPhoto).Any();
You can try to create an extension method as well for this
public static bool isSubset<T>(this IEnumerable<T> arr1, IEnumerable<T> arr2)
{
return !arr1.Except(arr2).Any();
}
You can use Except and check to see if there are any items in the resulting set
var containsAllElements = !preview_photo.Except(selPhoto).Any();
public ActionResult AddtoCart(string selPhoto, string preview_photo)
{
string[] values = selPhoto.Split(',');
string[] photo = preview_photo.Split(',');
foreach (var item in values)
{
if (photo.Contains(item))
{
// do action item in second array
}
else
{
//do action item not in second array
}
}
}
var y = new[] { "419", "418" };
var x = new[] { "418" };
bool present=y.ToList().TrueForAll(a=>x.Contains(a));
however its always good to show what you have tried

Interleaved merge with LINQ?

I'm currently experimenting a bit with LINQ. Let's say I have two collections of identical length:
var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };
I would like to merge those two collections into one, but in an interleaved fashion. The resulting sequence should thus be:
"1", "a", "2", "b", "3", "c"
What I've come up with so far is a combination of Zip, an anonymous type and SelectMany:
var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
.SelectMany( fs => new string[] { fs.F, fs.S } );
Does anybody know of an alternate/simpler way to achieve such an interleaved merge with LINQ?
The example you provided can by made simpler by dispensing with the anonymous type:
var result = first.Zip(second, (f, s) => new[] { f, s })
.SelectMany(f => f);
Warning: this will skip trailing elements if the enumerations have different lengths. If you'd rather substitute in nulls to pad out the shorter collection, use Andrew Shepherd's answer below.
You could write your own Interleave extension method, like in this example.
internal static IEnumerable<T> InterleaveEnumerationsOfEqualLength<T>(
this IEnumerable<T> first,
IEnumerable<T> second)
{
using (IEnumerator<T>
enumerator1 = first.GetEnumerator(),
enumerator2 = second.GetEnumerator())
{
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
yield return enumerator1.Current;
yield return enumerator2.Current;
}
}
}
The given implementation in the accepted answer has an inconsistency:
The resulting sequence will always contain all elements of the first sequence (because of the outer while loop), but if the second sequence contains more elements, than those elements will not be appended.
From an Interleave method I would expect that the resulting sequence contains
only 'pairs' (length of resulting sequence: min(length_1, length_2) * 2)), or that
the remaining elements of the longer sequence are always appended (length of resulting sequence: length_1 + length_2).
The following implementation follows the second approach.
Note the single | in the or-comparison which avoids short-circuit evaluation.
public static IEnumerable<T> Interleave<T> (
this IEnumerable<T> first, IEnumerable<T> second)
{
using (var enumerator1 = first.GetEnumerator())
using (var enumerator2 = second.GetEnumerator())
{
bool firstHasMore;
bool secondHasMore;
while ((firstHasMore = enumerator1.MoveNext())
| (secondHasMore = enumerator2.MoveNext()))
{
if (firstHasMore)
yield return enumerator1.Current;
if (secondHasMore)
yield return enumerator2.Current;
}
}
}
You can just loop and select the array depending on the index:
var result =
Enumerable.Range(0, first.Length * 2)
.Select(i => (i % 2 == 0 ? first : second)[i / 2]);
var result = first.SelectMany( ( f, i ) => new List<string> { f, second[ i ] } );
This is a modified version of the answer from #Douglas. This allows for a dynamic number of IEnumerables to interleave, and goes through all elements, not stopping at the end of the shortest IEnumerable.
public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] enumerables)
{
var enumerators = enumerables.Select(e => e.GetEnumerator()).ToList();
while (enumerators.Any())
{
enumerators.RemoveAll(e => {
var ended = !e.MoveNext();
if (ended) e.Dispose();
return ended;
});
foreach (var enumerator in enumerators)
yield return enumerator.Current;
}
}

Categories