Home > Software engineering >  Linq (or C# code) to Self join a table to fill null/empty values
Linq (or C# code) to Self join a table to fill null/empty values

Time:01-22

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:

result

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:

result

  •  Tags:  
  • Related