This is my list
int specifiedvalue=6.5;
Name Value
A 6.5
A 6.0
B 6.5
B 6.0
C 7.75
D 7.0
I would like to remove from this list objects which have the same name and a different value than the specifiedvalue(6.5) and keep the rest.
The result should be like:
A 6.5
B 6.5
C 7.75
D 7.0
Thanks
First of all you wrote that, the type of the specifiedValue is int. But the vaue is floating point and you must change it as double.
I am supposing that, you have declared your class like that:
public class Lens
{
public string Name { get; set; }
public double Value { get; set; }
public Lens(string name, double value)
{
Name = name;
Value = value;
}
}
Here I am initializing the objects:
double specidifedValue = 6.5;
List<Lens> pairs = new List<Lens>();
pairs.Add(new Lens("A", 6.5));
pairs.Add(new Lens("A", 6.0));
pairs.Add(new Lens("B", 6.5));
pairs.Add(new Lens("B", 6.0));
pairs.Add(new Lens("C", 7.75));
pairs.Add(new Lens("D", 7.0));
And with that, firstly I am finding Names which are occured more than one time in the list. And then selecting those with the value 6.5.
var keysMoreThanOne = pairs.GroupBy(x => x.Name)
.Where(x => x.Count() > 1).Select(x => x.Key).ToList();
List<Lens> filteredPairs = pairs
.Where(x => (keysMoreThanOne.Contains(x.Name) && x.Value == specidifedValue)
|| !keysMoreThanOne.Contains(x.Name)).ToList();
The result is as you want.
Update:
var result = new List<Lens>();
var keysMoreThanOne = pairs.GroupBy(x => x.Name).Where(x => x.Count() > 1).Select(x => x.Key).ToList();
if (specidifedValue > 0)
{
result = pairs.Where(x => (keysMoreThanOne.Contains(x.Name) && x.Value == specidifedValue) ||
!keysMoreThanOne.Contains(x.Name)).ToList();
}
else
{
result = pairs.Where(x => (keysMoreThanOne.Contains(x.Name) &&
x.Value == pairs.Where(y=> y.Name==x.Name).OrderByDescending(y=> y.Value).First().Value)
|| !keysMoreThanOne.Contains(x.Name)).ToList();
}
internal class NameValue
{
public string Name { get; set; }
public double Value { get; set; }
}
var sourceList = new List<NameValue>
{
new NameValue {Name = "A", Value = 6.5},
new NameValue {Name = "A", Value = 6.0},
new NameValue {Name = "B", Value = 6.5},
new NameValue {Name = "B", Value = 6.0},
new NameValue {Name = "C", Value = 7.75},
new NameValue {Name = "D", Value = 7.0}
};
var result = sourceList.GroupBy(x => x.Name)
.Select(x => new
{
Name = x.Key,
Value = x.Max(y => y.Value)
});
Related
So I am trying to group a list of ErrorItem into a list of ValidationFormatErrorDTO, depending on either the FieldType or the ValidationFormat.
The goal is to have a list of ValidationFormatErrorDTO containing only one type of errors.
The final object. So I am trying to have a list of it:
public class ValidationFormatErrorDTO
{
public ValidationFormat ValidationFormat { get; set; }
public FieldType FieldType { get; set; }
public List<ValidationFormatErrorItemDTO> ErrorItems { get; set; }
}
public class ValidationFormatErrorItemDTO
{
public int LineNumber { get; set; }
public string LineValue { get; set; }
}
Examples of error objects:
example 1:
var error = new ErrorItem
{
LineNumber = 2,
LineValue = "Test",
FieldType = FieldType.DateTime,
ValidationFormat = null
};
example 2:
error = new ErrorItem
{
LineNumber = 11,
LineValue = "john.doe.test.com",
ValidationFormat = ValidationFormat.Email,
FieldType = null
};
example 3:
error = new ErrorItem
{
LineNumber = 32,
LineValue = "1212",
ValidationFormat = ValidationFormat.PhoneNumber,
FieldType = null
};
And from here I'm stuck.
I've tried this:
var test = tempListErrors.GroupBy(x => x.ValidationFormat)
.Select(y => new ValidationFormatErrorDTO
{
ValidationFormat = y.Key,
ErrorItems = ??
}).ToList();
But first of all it means I should do the same for errors based on the FieldType. Which would give me 2 lists of ValidationFormatErrorDTO I would the have to join into 1 list.
And second, with this method I'm stuck with an IGrouping<ValidationFormat, ErrorItem> I don't know how to deal with.
I any of you think about how to do it with from where I am right now, or think about a better solution, that would be awesome!
Thanks for your time!
EDITS:
So, according to #Ashkan's answer, I would have this:
var formatErrors = tempListErrors.GroupBy(x => x.ValidationFormat)
.Select(y => new ValidationFormatErrorDTO
{
ValidationFormat = y.Key,
ErrorItems = y.Select(x => new ValidationFormatErrorItemDTO
{
LineNumber = x.LineNumber,
LineValue = x.LineValue
}).ToList()
}).ToList();
var fieldTypeErrors = tempListErrors.GroupBy(x => x.FieldType)
.Select(y => new ValidationFormatErrorDTO
{
FieldType = y.Key,
ErrorItems = y.Select(x => new ValidationFormatErrorItemDTO
{
LineNumber = x.LineNumber,
LineValue = x.LineValue
}).ToList()
}).ToList();
Any idea on how I can now merge those two?
Or how to group by both ValidationFormatand FieldType to get only one list?
Something like this?
var both = tempListErrors.GroupBy(x => new { x.ValidationFormat, x.FieldType })
.Select(y => new ValidationFormatErrorDTO
{
ValidationFormat = y.Key.ValidationFormat,
FieldType = y.Key.FieldType,
ErrorItems = y.Select(x => new ValidationFormatErrorItemDTO
{
LineNumber = x.LineNumber,
LineValue = x.LineValue
}).ToList()
}).ToList();
IGrouping I don't know how to deal with.
According to this specs it means that
Represents a collection of objects that have a common key
So from that point, you can get a list of ErrorItems like this
ErrorItems = group.Select(item => new ValidationFormatErrorItemDTO
{
LineNumber = item.LineNumber,
LineValue = item.LineValue
}).ToList()
Updated
var test = tempListErrors.GroupBy(p => new { p.ValidationFormat, p.FieldType})
.Select(group => new ValidationFormatErrorDTO
{
ValidationFormat = group.Key.ValidationFormat,
ErrorItems = group.Select(item => new ValidationFormatErrorItemDTO
{
LineNumber = item.LineNumber,
LineValue = item.LineValue
}).ToList()
}).ToList();
as y is the each group, so you can perform a .Select() on y to get a List<ValidationFormatErrorItemDTO>:
var test = tempListErrors.GroupBy(x => x.ValidationFormat)
.Select(y => new ValidationFormatErrorDTO
{
ValidationFormat = y.Key,
ErrorItems = y.Select(x => new ValidationFormatErrorItemDTO
{
LineNumber = x.LineNumber,
LineValue = x.LineValue
}).ToList()
}).ToList();
Drawing on Loop Through An Objects Properties In C# and Using LINQ to loop through inner class properties in outer class collection
Where you have objects (Phase) in a collection (PhaseRepo), I believe it is possible to specify propertiesOfInterest in the objects (Phase) and create a Dictionary to summarise the properties.
Please find below my attempt in LinqPad. Please assist with the syntax or advise an alternate approach.
Thank you
enum Dir {Up, Dn}
struct BmkKey
{
public Dir Direction;
public string DetailType;
}
class Phase
{
public Dir Direction { get; set; }
public double StartToTerminationBars { get; set; }
public double StartToTerminationPriceChange { get; set; }
public double StartToTerminationGa { get; set; }
public double NotRequiredProperty { get; set; }
}
class PhaseRepo
{
public List<Phase> Phases { get; private set; }
public List<Phase> GetPhases()
{
return new List<Phase>()
{
new Phase() { Direction = Dir.Up, StartToTerminationBars = 3.0, StartToTerminationPriceChange = 4.0, StartToTerminationGa = 4.0},
new Phase() { Direction = Dir.Up, StartToTerminationBars = 6.0, StartToTerminationPriceChange = 8.0, StartToTerminationGa = 4.0},
new Phase() { Direction = Dir.Dn, StartToTerminationBars = 3.0, StartToTerminationPriceChange = -4.0, StartToTerminationGa = -4.0},
new Phase() { Direction = Dir.Dn, StartToTerminationBars = 6.0, StartToTerminationPriceChange = -8.0, StartToTerminationGa = -4.0},
};
}
}
void Main()
{
var phaseRepo = new PhaseRepo();
var phases = phaseRepo.GetPhases();
//phases.Dump();
var propertiesOfInterest = typeof (Phase).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.Name == "StartToTerminationBars"
|| prop.Name == "StartToTerminationPriceChange"
|| prop.Name == "StartToTerminationGa")
.ToList();
//propertiesOfInterest.Dump();
// Please Help...
var test = propertiesOfInterest
.SelectMany(propertyInfo => phases
.Select(phase => phase)
.Select(keyValuePair => new
{
phase.Direction,
keyValuePair.Key,
keyValuePair.Value
})
.Select(arg => new
{
Key = new BmkKey
{
Direction,
DetailType = propertyInfo.Name
},
Value = (double)propertyInfo.GetValue(arg.Value, null)
}))
.GroupBy(grp => grp.Key)
.ToDictionary(grp => grp.Key, grp => x => x.ToList());
test.Dump();
}
Expected output:
Solution
Replace the part following
//Please Help...
with the following:
var test = propertiesOfInterest
.SelectMany(propertyInfo => phases
.Select(phase => new
{
phase.Direction,
propertyInfo.Name,
Value = (double)propertyInfo.GetValue(phase)
}))
.GroupBy(o => new BmkKey { Direction = o.Direction, DetailType = o.Name })
.ToDictionary(grp => grp.Key, grp => grp.Select(x => x.Value));
This essentially gives the expected output shown in your question. (The order is different; adjust using OrderBy() as your needs require.)
Explanation
There were a few issues in your code, but the primary problems were:
The call to GetValue() invoked on the wrong object
The key selector in the .ToDictionary() call.
In addition, the following are not wrong, but unnecessary:
The .Select(phase => phase) call, which does nothing--like a line of code specifying x = x;
Building the key before grouping. Instead, you can simply define the key during the .GroupBy() operation.
I have a structure like
class a
{
public IList<b> bs{ get; set; }
public class b
{
public string r{ get; set; }
public IList<sl> sls{ get; set; }
public class sl
{
public string sn{ get; set; }
public string st{ get; set; }
}
}
}
the query is like if sn == "abc" then get r
I have done
a aobj = new a();
var aa = aobj.bs.Where(c => c.sl != null).Select(c => c).ToList(); // here I get `r = "qwerty", sls will have data like sn = "qwerty0", st= "1" ; sn = "asdf" , st="2"; sn = "zxc" st = "abc"; sn="me" , st = "abc"
var bb = aa.where(c => c.sl.Select(dr => dr.st.ToLower().Contains("abc"))); // I 'm here checking that `sn` contain abc or not
var cc = bb.Select(c => c.r).ToList(); // result
my expected output of query is "zxc", "me"
but I am getting all the list not only contains abc.. can anyone suggest me what should I do? I am partitioning this query to debug.
Thank you
You'll need to use the Any operator to check if an enumerable collection has an item that meets a criteria.
You can't use Select as that only projects an item, it isn't returning an predicate and as such has no function in a where clause.
Here is your (fixed for syntax errors) changed code:
var aa = aobj.bs.Where(c => c.sls != null).Select(c => c).ToList();
// use Any here
var bb = aa.Where(c => c.sls.Any(dr => dr.sn.ToLower().Contains("abc")));
var cc = bb.Select(c => c.r).ToList();
And here is the test set I used:
a aobj = new a();
aobj.bs = new List<b>();
aobj.bs.Add(new b {
r ="bar",
sls = new List<sl>{
new sl { sn="tets"},
new sl { sn="no"}
}
});
aobj.bs.Add(new b {
r ="foo",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
aobj.bs.Add(new b {
r ="fubar",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
This will output:
foo
fubar
If you combine all operators together you'll get:
var merged = aobj
.bs
.Where(c => c.sls != null
&& c.sls.Any(dr => dr.sn.ToLower().Contains("abc")))
.Select(c => c.r);
I think you can use a code like this:
var cc = a.bs
.Where(w => w.sls?.Any(s => s.st?.ToLower().Contains("abc") ?? false) ?? false)
.Select(c => c.r);
I have settings class
public class Setting
{
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
and i have to list
IEnumerable<Setting> A1 => contain {"A","1"}{"B","2"}
IEnumerable<Setting> A2 => contain {"A","1"}{"B","5"}
i want linq statment to chose the element from list A2 that have same key and different value here is {"B","5"}
I have try
A2.Where(x => A1.Any(y => y.Value != x.Value)).ToList();
this give me the two elemnts in A2
can any one help me
thank you
**Edit **
my settings class
public class Setting : Entity<int>
{
public virtual DateTime? ModificationDate { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
public virtual string ModifiedBy { get; set; }
public virtual string Type { get; set; }
public virtual string ValidateRegex { get; set; }
public virtual bool IsSystem { get; set; }
}
and i have return from mvc IEnumerable<Setting> let it name settings,
then i get from database the original settings IEnumerable<Setting> let it name dbsettings
i want to know the changed value from settings to make update on it
You need to compare the Key as well:
A2.Where(x => A1.Any(y => y.Key == x.Key && y.Value != x.Value)).ToList();
The following sample returns { "B", "5" } as the result:
void Main()
{
var a1 = new List<Setting>(new Setting[] {
new Setting() { Key = "A", Value = "1" },
new Setting() { Key = "B", Value = "2" } });
var a2 = new List<Setting>(new Setting[] {
new Setting() { Key = "A", Value = "1" },
new Setting() { Key = "B", Value = "5" } });
var result = a2.Where(x => a1.Any(y => y.Key == x.Key && y.Value != x.Value)).ToList();
Console.WriteLine(result);
}
As you are comparing strings, you should be aware that == and != respectively always compares case-sensitive. So the keys need to be written in the same way in both lists (and also differences in case will be recognized as relevant differences). You can also use an overload of string.Compare to specify the comparison options in greater detail.
This should do it:
A2.Where(x => A1.Any(y => y.Key == x.Key && y.Value != x.Value))
BTW, your Setting class seems like reinventing the wheel. Dictionary, Tuple and NameValueCollection can all do that for you.
var A1 = new List<Setting>(){new Setting(){Key = "A", Value = "1"}};
var A2 = new List<Setting>() { new Setting() { Key = "A", Value = "2" } };
var a1Keys = A1.Select(x => x.Key).ToList();
var dupKeys = A2.Where(x => a1Keys.Contains(x.Key)).Select(x=>x.Key);
var res = A2.Where(x => dupKeys.Contains(x.Key));
For performance reasons you could use a lookup:
var a1KeyLookup = A1.ToLookup(x => x.Key);
List<Setting> a2List = A2
.Where(a2 => a1KeyLookup[a2.Key].Any(a1 => a1.Value != a2.Value))
.ToList();
Here's your sample data:
IEnumerable<Setting> A1 = new List<Setting> {
new Setting{Key="A", Value="1"},
new Setting{Key="B", Value="2"},
};
IEnumerable<Setting> A2 = new List<Setting> {
new Setting{Key="A", Value="1"},
new Setting{Key="B", Value="5"},
};
var a1KeyLookup = A1.ToLookup(x => x.Key);
List<Setting> a2List = A2
.Where(a2 => a1KeyLookup[a2.Key].Any(a1 => a1.Value != a2.Value))
.ToList();
It returns as expected a list with a single item: Key="B", Value="5"
I would like to get the count of a number of entity tables and assign them to a single object which holds the counted values.
I am using a union because I want to execute a single query against the database.
I have written the following code but this will return a separate counts view model for each group by, instead Id like to assign the values to the properties of a single counts view model.
var counts =
_db.Departments.All()
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.All().Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => new CountsVm()
{
DepartmentCount = x.Count(d => d.key == 1),
StudentCount = x.Count(s => s.key == 2)
});
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}
Here is a solution which will produce one query
var countsQuery =
_db.Departments.All()
.Select(p => new { key = 1, count = 0 })
.Union(_db.Students.All().Select(p => new { key = 2, count = 0 }))
.GroupBy(p => p.key)
.Select(p => new { key = p.Key, count = p.Count() }).ToList();
var counts = new CountsVm()
{
DepartmentCount =
countsQuery.Where(p => p.key == 1)
.Select(p => p.count)
.FirstOrDefault(),
StudentCount =
countsQuery.Where(p => p.key == 2)
.Select(p => p.count)
.FirstOrDefault()
};
Do you just need to call count on each entry table separately?
var counts = new CountsVm()
{
DepartmentCount = _db.Departments.All().Count(),
StudentCount = _db.Students.All().Count()
};
If I understand it correctly you could do something like: (i've done only using linq, but the return null inside a select it's not a good practice). A foreach would server you better)
var countsVm = new CountsVm(){
DepartmentCount = 0,
StudentCount = 0
};
var counts =
_db.Departments.All()
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.All().Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => {
countsVm.DepartmentCount += x.Count(d => d.key == 1);
countsVm.StudentCount += x.Count(s => s.key == 2);
return null;
});
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}
Try to remove All from query and run FirstOrDefault()
var counts =
_db.Departments.
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => new CountsVm()
{
DepartmentCount = x.Count(d => d.key == 1),
StudentCount = x.Count(s => s.key == 2)
}).FirstOrDefault();
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}