let's assume I have a list which has elements with missing values but matching id(which may or not may be missing so I would like to take the values which arent null or empty and put them into a single element which in the matching field name converts to a IGrouping/list if there are several values which arent null or a single null value if there none,
this is an example
public class MyClass
{
public int vali;
public string vala;
public string valb;
public long? vall;
}
var list = new List<MyClass>()
{
new MyClass(){vali= 2,vala=null,valb="how are you",vall=7},
new MyClass {vali=3,vala="hi",valb="how are you doing",vall=null},
new MyClass{vali=2,vala="hello",valb="how are you",vall=null},
new MyClass{vali=3,vala=null,valb=null,vall=8},
new MyClass(){vali= 2,vala=null,valb=null,vall=7},
};
I would like to get the following output
{2,"hello",string[] {"how are you","how are you" },int[] {7,7} }
{3 , "hi" , "how are you doing" ,8 },
using linq
list.GroupBy(x=>x.vali).Select(x=>x.FirstOrDefault()).ToList();
returns
{2,"how are you",null,7},
{3, "how are you doing",null}
so what query could I use or how could I implement to return a list(or a IGrouping as linq does but writing my own algorithm)
Thank you
CodePudding user response:
Try following :
class Program
{
static void Main(string[] args)
{
var list = new List<MyClass>()
{
new MyClass(){vali= 2,vala=null,valb="how are you",vall=7},
new MyClass {vali=3,vala="hi",valb="how are you doing",vall=null},
new MyClass{vali=2,vala="hello",valb="how are you",vall=null},
new MyClass{vali=3,vala=null,valb=null,vall=8},
new MyClass(){vali= 2,vala=null,valb=null,vall=7},
new MyClass(){vali= 2,vala=null,valb="how are you",vall=7},
new MyClass {vali=3,vala="hi",valb="how are you doing",vall=null},
new MyClass{vali=2,vala="hello",valb="how are you",vall=null},
new MyClass{vali=3,vala=null,valb=null,vall=8},
new MyClass(){vali= 2,vala=null,valb=null,vall=7},
};
var results = list.GroupBy(x => x).ToList();
}
}
public class MyClass : IEquatable<MyClass>
{
public int vali;
public string vala;
public string valb;
public long? vall;
public bool Equals(MyClass other)
{
return
(this.vali == other.vali) &&
(this.vala == other.vala) &&
(this.valb == other.valb) &&
(this.vall == other.vall);
}
public override bool Equals(object obj)
{
return Equals(obj as MyClass);
}
public override int GetHashCode()
{
return (this.vali.ToString() "^" this.vala "^" this.valb "^" this.vall.ToString()).GetHashCode();
}
}
CodePudding user response:
I think I have found what I wanted
with the following query
var
list2=list.ToDictionary(x=>x).GroupBy(x=>x.Key.vali).Select(x=>new {
vali = x.First().Key.vali,
vala = x.Count(v => v.Value.vala !=null) > 0 ?x.Where(x=>x.Value.vala!=null).Select(x=>x.Value.vala).ToList() : new List<string> { x.FirstOrDefault(x=>x.Value.vala!=null).Value.vala },
valb = x.Count(v => v.Value.valb !=null) > 0 ?x.Where(x=>x.Value.valb!=null).Select(x=>x.Value.valb).ToList() : new List<string> { x.FirstOrDefault(x=>x.Value.valb!=null).Value.valb },
vall = x.Count(v => v.Value.vall !=null) > 0 ?x.Where(x=>x.Value.vall!=0).Select(x=>x.Value.vall).ToList() : new List<long?> { x.FirstOrDefault().Value.vall },
}).ToList();
CodePudding user response:
You can't get a mixed output with some values having an array and other having a single value. It's all one way or the other.
It seems to me that this does what you want:
var result =
list
.GroupBy(x => x.vali)
.Select(gxs => new
{
vali = gxs.Key,
vala = gxs.Select(gx => gx.vala).Where(x => x != null).ToList(),
valb = gxs.Select(gx => gx.valb).Where(x => x != null).ToList(),
vall = gxs.Select(gx => gx.vall).Where(x => x != null).ToList(),
})
.ToList();
I get:
Otherwise, to get single values, try this:
List<MyClass> result =
list
.GroupBy(x => x.vali)
.Select(gxs => new MyClass()
{
vali = gxs.Key,
vala = gxs.Select(gx => gx.vala).Aggregate((x, y) => x ?? y),
valb = gxs.Select(gx => gx.valb).Aggregate((x, y) => x ?? y),
vall = gxs.Select(gx => gx.vall).Aggregate((x, y) => x ?? y),
})
.ToList();
Using your data, I get this result:


