I have a page that can pass anywhere from 1 array to 3 arrays.
My current code works but only if each array is populated with items.
[HttpPost, ValidateInput(false)]
public ActionResult AddCampaign(string campaignName, string editor1, Int32[]
productImages, Int32[] lifestyleImages, Int32[] logoImages)
{
// Add image list to CampaignImages table
// Combine image array list into a single array
var imageList = productImages.Concat(lifestyleImages).Concat(logoImages).ToArray();
return RedirectToAction("AddCampaign", "Admin");
}
Each array is the exact same it will only hold an ID of an image. This will work as long as all 3 arrays have something in them. But if one is null it errors out.
What I need is a single array that is a combination of all 3 arrays, productImages, lifestyleImages, logoImages.
Not sure what I am missing.
Well you're missing the fact that Concat will throw an exception if you pass it a null reference... but it's easy enough to fix. Either write an extension method like this:
public static IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> src)
{
return src ?? Enumerable.Empty<T>();
}
And call it as:
var imageList = productImages.NullToEmpty()
.Concat(lifestyleImages.NullToEmpty())
.Concat(logoImages.NullToEmpty())
.ToArray();
... or just use the null-coalescing operator directly:
var empty = Enumerable.Empty<int>();
var imageList = (productImages ?? empty).Concat(lifeStyleImages ?? empty)
.Concat(logoImages ?? empty)
.ToArray();
Related
Before marking this as duplicate because of its title please consider the following short program:
static void Main()
{
var expected = new List<long[]> { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected)) throw new Exception();
}
static IEnumerable<long[]> DoSomething()
{
yield return new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
}
I have a method which returns a sequence of arrays of type long. To test it I wrote some test-code similar to that one within Main.
However I get the exception, but I don´t know why. Shouldn´t the expected sequence be comparable to the actually returned one or did I miss anything?
To me it looks as both the method and the epxected contain exactly one single element containing an array of type long, doesn´t it?
EDIT: So how do I achieve to not get the exception meaning to compare the elements within the enumeration to return equality?
The actual problem is the fact that you're comparing two long[], and Enumerable.SequenceEquals will use an ObjectEqualityComparer<Int64[]> (you can see that by examining EqualityComparer<long[]>.Default which is what is being internally used by Enumerable.SequenceEquals), which will compare references of those two arrays, and not the actual values stored inside the array, which obviously aren't the same.
To get around this, you could write a custom EqualityComparer<long[]>:
static void Main()
{
var expected = new List<long[]>
{ new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected, new LongArrayComparer()))
throw new Exception();
}
public class LongArrayComparer : EqualityComparer<long[]>
{
public override bool Equals(long[] first, long[] second)
{
return first.SequenceEqual(second);
}
// GetHashCode implementation in the courtesy of #JonSkeet
// from http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
public override int GetHashCode(long[] arr)
{
unchecked
{
if (array == null)
{
return 0;
}
int hash = 17;
foreach (long element in arr)
{
hash = hash * 31 + element.GetHashCode();
}
return hash;
}
}
}
No, your sequences are not equal!
Lets remove the sequence bit, and just take what is in the first element of each item
var firstExpected = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
var firstActual = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
Console.WriteLine(firstExpected == firstActual); // writes "false"
The code above is comparing two separate arrays for equality. Equality does not check the contents of arrays it checks the references for equality.
Your code using SequenceEquals is, essentially, doing the same thing. It checks the references in each case of each element in an enumerable.
SequenceEquals tests for the elements within the sequences to be identical. The elements within the enumerations are of type long[], so we actually compare two different arrays (containing the same elements however) against each other which is obsiously done by comparing their references instead of their actual value .
So what we actually check here is this expected[0] == actual[0] instead of expected[0].SequqnceEquals(actual[0])
This is obiosuly returns false as both arrays share different references.
If we flatten the hierarchy using SelectMany we get what we want:
if (!actual.SelectMany(x => x).SequenceEqual(expected.SelectMany(x => x))) throw new Exception();
EDIT:
Based on this approach I found another elegant way to check if all the elements from expected are contained in actual also:
if (!expected.All(x => actual.Any(y => y.SequenceEqual(x)))) throw new Exception();
This will search if for ever sub-list within expected there is a list within actual that is sequentially identical to the current one. This seems much smarter to be as we do not need any custom EqualityComparer and no weird hashcode-implementation.
public class Stuff
{
public int x;
// ... other stuff
}
I have a IEnumerable<Stuff> and I want to build a int[] of all of the x properties of all the Stuff objects in the collection.
I do:
IEnumerable<Stuff> coll;
// ...
var data = coll.Select(s => s.x).ToArray();
What I want is a null array rather than a int[0] if the collection is empty. In other words, if !coll.Any(), then I want data = null. (My actual need is that coll is an intermediate result of a complex LINQ expression, and I would like to do this with a LINQ operation on the expression chain, rather than saving the intermediate result)
I know that int[0] is more desirable than null in many contexts, but I am storing many of these results and would prefer to pass around nulls than empty arrays.
So my current solution is something like:
var tmp = coll.Select(s => s.x).ToArray();
int[] data = tmp.Any() ? tmp : null;
Any way to do this without storing tmp?
EDIT: The main question is how to do this without storing intermediate results. Something like NULLIF() from T-SQL where you get back what you passed in if the condition is false, and NULL if the condition is true.
If you're doing this a lot, you could write an extension method:
public static class IEnumerableExt
{
public static T[] ToArrayOrNull<T>(this IEnumerable<T> seq)
{
var result = seq.ToArray();
if (result.Length == 0)
return null;
return result;
}
}
Then your calling code would be:
var data = coll.Select(s => s.x).ToArrayOrNull();
Create the array only if coll is not empty, so the other way round:
int[] data = null;
if(coll.Any()) data = coll.Select(s => s.x).ToArray();
There's not a way to get Select to return null, but if you don't want to create an additional array you could do:
var tmp = coll.Select(s => s.x);
int[] data = tmp.Any() ? tmp.ToArray() : null;
Given the following C# code:
List<string> source = new List<string>();
IEnumerable<string> values = from value in source select value;
Will values ever be null or will it always return an empty sequence?
Yes it CAN return null if you have an extension method defined in your code somewhere like the following:
public static IEnumerable<string> Select(this List<string> list, Func<string, string> action)
{
return null;
}
Otherwise no; it will return an empty sequence.
The values sequence itself will never be null. If source is empty then values will be an empty sequence containing no items.
(And, of course, it's possible that one or more of the string items in the sequence might be null.)
Linq returns empty sequences
If you want to test if the sequence is empty use the .Any() method
How to remove my value in String Array and how i can rearrange
public string[] selNames = new string[5];
selNames[0]="AA";
selNames[1]="BB";
selNames[2]="CC";
selNames[3]="DD";
selNames[4]="EE";
In certain Conditaion i need to Check for the existing value and i want to remove it from my collection, How i can do it.
i tried like below, but i cannot, it returns true, but how to make that index value to null
If(selNames .Contains("CC").ToString()==true)
{
// how to make that index null which contains the "CC"; and i need to rearrage the array
}
You can do following.
var newArray = selNames.Where(s => s != "CC").ToArray();
where s is the arg of the Func<TSource, bool> delegate TSource is string in your case.
So it will compare each string in array and return all which is not "СС"
here is a link to msdn
You can use the 'List< T >' for checking the existing values and also can remove the item from the list and also can arrange the list.
The following is the code snippet:
List<string> list = new List<string>();
list.Add("AA");
list.Add("BB");
list.Add("CC");
list.Add("DD");
list.Add("EE");
list.Add("FF");
list.Add("GG");
list.Add("HH");
list.Add("II");
MessageBox.Show(list.Count.ToString());
list.Remove("CC");
MessageBox.Show(list.Count.ToString());
I think that this problem can be sorted using reflection (a technology which I'm not too sure about).
My code is receiving some code objects that have been serialised to XML at runtime. When I receive it and deserialise it one field is causing me some hassle.
There is a field that can contain a combination of the following data classes (simplified for clarity):
class KeyValuePairType
{
public string Key;
public string Value;
}
class KeyValueListPair
{
public string Key;
public string[] Value;
}
I receive these into my code as an object[] and I need to determine at runtime what exactly this contains so that I can call an interface on a local object that requires
KeyValuePairType[] and KeyValueListPair[] as parameters e.g.
public DoSomeWork(KeyValuePairType[] data1, KeyValueListPair[] data2)
I have the following cases to cope with:
object[] contains:
nothing in which case I call
DoSomeWork(null,null);
an array of KeyValuePairType only,
in which case I call
DoSomeWork(KeyValuePairType[],
null);
an array of KeyValueListPair only,
in which case I call
DoSomework(null,
KeyValueListPair[]);
or an array of each, in which case I
call DoSomework(KeyValuePairType[],
KeyValueListPair[]);
Any ideas are welcome.
Thank you
It turns out that the object array contains a random sequence of discrete objects. Initially I was led to belive that it may be a sequence of discretes and arrays of those objects.
As it is the LINQ statements will cover all eventualities.
Can I say a big thank you to those that that answered. I have posted a +1 for those answering with the LINQ statements.
Assuming you've got LINQ available to you...
public void Foo(object[] values)
{
var pairs = values.OfType<KeyValuePairType>().ToArray();
var lists = values.OfType<KeyValueListPair>().ToArray();
pairs = pairs.Length == 0 ? null : pairs;
lists = lists.Length == 0 ? null : lists;
DoSomeWork(pairs, lists);
}
You can do this using LINQ in C# 3, like this:
void HandleThings(params object[] values) {
var pairTypes = values.OfType<KeyValuePairType>().ToArray();
var listPairs = values.OfType<KeyValueListPair>().ToArray();
DoSomeWork(pairTypes.Any() ? pairTypes : null, listPairs.Any() ? listPairs : null);
}
You can make it a tiny bit faster by replacing .Any() with .Length > 0, at the cost of brevity.
How about this:
object[] objects = GetObjects();
var pairs = objects.OfType<KeyValuePairType[]>().FirstOrDefault();
var lists = objects.OfType<KeyValueListPair[]>().FirstOrDefault();
DoSomeWork(pairs, lists);
It depends, somewhat, on how the individual elements are being handled to you, but in general, the is and as keywords should work fine for checking individual objects in the object[] array, and assigning them to the appropriate output.
After making sure object[] is not null and has a length of greater than 1, you can just call GetType on the objects in the array.
objectArray[0].GetType().FullName
will return either
"Namespace.KeyValuePairType"
or
"Namespace.KeyValueListPair"