get array values using Linq (LinqPad) - c#

I have the following array and Linq statements. I can get the field name with the Linq, but how to I retrieve the field value? (in LinqPad)
var source = new[] {
new { FirstName = "Foo", SurName = "Bar", Password = "secret" },
};
var membersToInclude =
source
.First()
.GetType()
.GetProperties()
.Where(x => x.Name != "Password")
.Select(x => x.Name)
.ToArray();
foreach (var m in membersToInclude)
{
m.Dump();
}
The Dump() just displays the words -- FirstName, SurName --
How can I also get the values -- Foo and Bar?
While I'm at it (ok I'm asking 2 questions in one post -- but .... they are all linq --LinqPad related) how do you get string length using Linq (well, LinqPad)
from w in Albums.Take(5)
select new
{
w1 = w.AlbumArtUrl.Substring(1,5),
w2 = w.AlbumArtUrl.Length
}
The Substring function works fine, but when I try to get Length, LinqPad gives me this message
The specified argument value for the function is not valid. [ Argument # = 1,Name of function(if known) = LEN ]
I have tried using len, LEN, ... how to get the string length value?

You're selecting just property's name. You need to apply it to an object to get the value. This should give you an idea:
var source = new[] {
new { FirstName = "Foo", SurName = "Bar", Password = "secret" },
};
var membersToInclude =
source
.First()
.GetType()
.GetProperties()
.Where(x => x.Name != "Password")
.Select(x =>
{
var value = x.GetValue(source.First());
return new {x.Name, value};
})
.ToArray();
foreach (var m in membersToInclude)
{
m.Dump();
}

Related

How can I distinct with condition?

Here is class UserArrived:
public class UserArrived{
public string id{get;set;}
}
Here is class OldUser:
public class OldUser{
public string id{get;set;}
public DateTime lastArrived{get;set;}
}
And here is class User:
public class User{
public string id{get;set;}
public Boolean newUser{get;set;}
}
Finally, here is two List:
List<UserArrived> UserArrivedList=new List<UserArrived>();
List<OldUser> OldUserList=new List<OldUser>();
All the id in each class is unique.
Now I need to combine UserArrived and OldUser to a brand new List<User>.
As we know, the user arrives the shop may is a new user or an old user. If the user id in UserArrived also contains in OldUser, the property newUser in the new List is false for true.
In my opinion, I will combine two List into one first and then use the distinct method to remove the duplicates.
However, it seems the distinct can not run with a condition.
Although I can use several foreach to solve this while I feel it is so troublesome. I want to use something easy just like lambda or linq. How can I achieve this?
=============================
Here is an example of the input:
List<UserArrived> UserArrivedList=new List<UserArrived>(){new UserArrived(){id="A"},new UserArrived(){id="B"},new UserArrived(){id="C"}};
List<OldUser> OldUserList=new List<OldUser>(){new OldUser(){id="B",lastArrived=DateTime.Now}};
the output is:
A,true
B,false
C,true
If I understand your requirement you're saying that if an id is in both lists then the user is an old user, otherwise it is a new user.
So here's the simplest way that I could come up with to do it:
IEnumerable<User> users =
Enumerable
.Concat(
UserArrivedList.Select(i => i.id),
OldUserList.Select(i => i.id))
.ToLookup(x => x)
.Select(x => new User() { id = x.Key, newUser = x.Count() == 1 });
Let's test with some input:
var UserArrivedList = new List<UserArrived>()
{
new UserArrived() { id = "A" },
new UserArrived() { id = "B" },
};
var OldUserList = new List<OldUser>()
{
new OldUser() { id = "B" },
new OldUser() { id = "C" },
};
Here are my results:
B is the only user who appears in both lists so should be False.
So, there's a bit of confusion about the requirements here.
The OP has added a concrete example of the input data and the expected output.
var UserArrivedList = new List<UserArrived>()
{
new UserArrived() { id = "A" },
new UserArrived() { id = "B" },
new UserArrived() { id = "C" }
};
var OldUserList = new List<OldUser>()
{
new OldUser() { id = "B", lastArrived = DateTime.Now }
};
With this input the OP is expecting True, False, True for A, B, C respectively.
Here is the code of the four current answers:
var results = new []
{
new
{
answered = "Enigmativity",
users = Enumerable
.Concat(
UserArrivedList.Select(i => i.id),
OldUserList.Select(i => i.id))
.ToLookup(x => x)
.Select(x => new User() { id = x.Key, newUser = x.Count() == 1 })
},
new
{
answered = "JQSOFT",
users = UserArrivedList.Select(x => x.id)
.Concat(OldUserList.Select(y => y.id))
.Distinct()
.Select(x => new User
{
id = x,
newUser = OldUserList.Count(o => o.id == x) == 0,
})
},
new
{
answered = "Anu Viswan",
users =
UserArrivedList
.Join(OldUserList, ual => ual.id, oul => oul.id, (ual, oul) => new User { id = oul.id, newUser = false })
.Concat(UserArrivedList.Select(x => x.id).Except(OldUserList.Select(x => x.id))
.Concat(OldUserList.Select(x => x.id).Except(UserArrivedList.Select(x => x.id)))
.Select(x=> new User{ id = x, newUser = true}))
},
new
{
answered = "Barns",
users =
UserArrivedList.Select(i => i.id)
.Union(OldUserList.Select(i => i.id))
.Select(j => new User
{
id = j,
newUser =
!(UserArrivedList.Select(i => i.id).Contains(j)
&& OldUserList.Select(i => i.id).Contains(j))})
}
};
That gives the output of:
So, currently all of the answers presented match the OP's example.
I'd be interested in the OP commenting on this as the input data:
var UserArrivedList = new List<UserArrived>()
{
new UserArrived() { id = "A" },
new UserArrived() { id = "B" },
};
var OldUserList = new List<OldUser>()
{
new OldUser() { id = "B" },
new OldUser() { id = "C" },
};
When I run this I get this output:
Here three users match and one does not.
This all boils down to what the description means:
As we know, the user arrives the shop may is a new user or an old user. If the user id in UserArrived also contains in OldUser, the property newUser in the new List is false for true.
The thing about LINQ--it isn't always easy. In fact it can get quit cluttered. In the question statement I read,
I want to use something easy just like lambda or linq.
Well, that is relative. But, I think that when using LINQ, one should try to keep it simple. Even break the statement down into multiple statements if necessary. For that reason I propose this solution (demonstrated in a console app):
static void Main(string[] args)
{
Console.WriteLine();
Console.WriteLine("--------------------Test This Code -----------------------");
var combined = TestUserCombined();
//The following is just to demonstrate the list is populated properly
combined.OrderBy(s => s.id.PadLeft(4, '0')).ToList().ForEach(k => Console.WriteLine($"X id: {k.id} | isNew:{k.newUser}"));
}
private static IEnumerable<User> TestUserCombined()
{
List<UserArrived> userArrivedList=new List<UserArrived>();
List<OldUser> oldUserList=new List<OldUser>();
//populate the lists...
for(int i = 0; i < 20; i+=2)
{
var userArrived = new UserArrived();
userArrived.id = i.ToString();
userArrivedList.Add(userArrived);
}
for(int i = 0; i < 20; i+=3)
{
var oldUser = new OldUser();
oldUser.id = i.ToString();
oldUserList.Add(oldUser);
}
//Now for the solution...
var selectedUserArrived = userArrivedList.Select(i => i.id);
var selectedOldUser = oldUserList.Select(i => i.id);
var users = selectedUserArrived
.Union(selectedOldUser)
.Select(j => new User{id=j,newUser=!(selectedUserArrived.Contains(j) && selectedOldUser.Contains(j))});
return users;
}
Certainly, this all could have been done in one statement, but I believe this makes it more readable and understandable.
EDIT:
There has been some discussion amongst the coders posting solutions as to exactly what conditions must be met in order for the value "newUser" to be set to "true". It was my understanding from the initial posted question that the "id" must be present in both lists "UserArrivedList" AND "OldUserList", but I tend to agree with #JQSOFT that it makes more sense that the only condition that must be met should be that the "id" need only be present in "OldUserList". If that is indeed the case than the Select() expression above should be .Select(j => new User{id=j,newUser=!selectedOldUser.Contains(j)});
I hope I understood your query. One way to achieve this using Linq would be
var users = UserArrivedList.Join(OldUserList,ual=>ual.id,oul=>oul.id,(ual,oul)=>new User{id=oul.id,newUser=false})
.Concat(UserArrivedList.Select(x=>x.id).Except(OldUserList.Select(x=>x.id))
.Concat(OldUserList.Select(x=>x.id).Except(UserArrivedList.Select(x=>x.id)))
.Select(x=> new User{id=x,newUser=true}));
Now you need to create a distinct list of User type from two lists of different types; UserArrived and OldUser objects. A user is identified by a unique id of string type.
Accordingly, I'd suggest this:
var users = UserArrivedList.Select(x => x.id)
.Concat(OldUserList.Select(y => y.id))
.Distinct()
.Select(x => new User
{
id = x,
newUser = OldUserList.Count(o => o.id == x) == 0,
}).ToList();
Which gets the unique ids from both UserArrivedList and OldUserList and creates new User object for each. The OldUserList.Count(o => o.id == x) == 0, assigns false to the newUser property if the user id exists in the OldUserList otherwise true.

Looping through members of a defined concrete type?

I'm doing comparisons across databases (with about 20 fields) and I've defined a concrete type to handle the comparison.
If my comparison fails, I want to loop through the individual items in the catch block and provide the user a list errors. Below, I've done it manually through the first few variables. Is there a more efficient way to loop this through all 20 fields? I started with a foreach (Object objectItem .. but not sure if that's the right way to go.
Any thoughts or much needed guidance?
try {
CollectionAssert.IsSubsetOf(orgs, members, "Error Matching testlist Fields");
}
catch
{
//OrgID
var sourceOrgID = orgs.Select(o => o.OrgID);
var destOrgID = members.Select(o => o.OrgID);
var errorList1 = sourceOrgID.Except(destOrgID);
string failedTests = null;
failedTests = string.Join("\n", errorList1);
Assert.IsTrue(0 == failedTests.Length, "The following Org IDs are not contained in the source: \n" + failedTests);
//DealerCode
var sourceDealerCode = orgs.Select(o => o.DealerCode);
var destDealerCode = members.Select(o => o.DealerCode);
var errorList2 = sourceDealerCode.Except(destDealerCode);
failedTests = null;
failedTests = string.Join("\n", errorList2);
Assert.IsTrue(0 == failedTests.Length, "The following Dealer Codes are not contained in the source: \n" + failedTests);
//orgkey
var sourceOrgKey = orgs.Select(o => o.OrgKey);
var destOrgKey = members.Select(o => o.OrgKey);
var errorList3 = sourceOrgKey.Except(destOrgKey);
failedTests = null;
failedTests = string.Join("\n", errorList3);
Assert.IsTrue(0 == failedTests.Length, "The following Org Keys are not contained in the source: \n" + failedTests);
You need reflection to do this:
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
GetColumn(orgList,property.Name);
}
var names = items.Select(x => x.GetType().GetProperty("prpname").GetValue(x));
public IEnumerable<object> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x =>
x.GetType().GetProperty(columnName).GetValue(x));//u can put your test code heere and you can loop it through object properties
}
you can create a list to store rslt and add results to the list else you can write output in logs files
If I haven't misunderstood your question, you could do something like this using shouldly
[TestMethod]
public void UnitTestExample()
{
var orgs = new Organisation
{
Ids = new List<int>{ 1,3 },
DealerCodes = new List<string> { "foo","bar"}
};
var members = new Organisation
{
Ids = new List<int> { 1,2,3 },
DealerCodes = new List<string> { "foo", "bar", "buzz" }
};
orgs.ShouldSatisfyAllConditions(
() => orgs.Ids.ShouldBe(members.Ids, ignoreOrder: true),
() => orgs.DealerCodes.ShouldBe(members.DealerCodes, ignoreOrder: true)
);
}
The output produced is:
You would still have to specify every property you want to check inside ShouldSatisfyAllConditions but shouldly does all the heavy lifting around comparing the lists and output the differences.
I'm assume that:
orgs is a IQueryable<T1>
members is a IQueryable<T2>
typeof(T1) == typeof(T2)
If so, that function could help:
private static void CompareRecords<TEntity>(IQueryable<TEntity> orgs, IQueryable<TEntity> members)
{
var entityType = typeof (TEntity);
var selectMethod = typeof (Queryable).GetMethods().First(x => x.Name == "Select");
var exceptMethod = typeof(Queryable).GetMethods().First(x => x.Name == "Except");
var toArrayMethod = typeof (Enumerable).GetMethods().First(x => x.Name == "ToArray");
foreach (var property in entityType.GetProperties())
{
var paramExpr = Expression.Parameter(entityType, "x");
var propExpr = Expression.Property(paramExpr, property.Name);
var delegateType = typeof (Func<,>).MakeGenericType(entityType, property.PropertyType);
var lambdaExpr = Expression.Lambda(delegateType, propExpr, paramExpr);
var parameterizedSelectMethod = selectMethod.MakeGenericMethod(entityType, property.PropertyType);
var parameterizedExceptMethod = exceptMethod.MakeGenericMethod(property.PropertyType);
var source = parameterizedSelectMethod.Invoke(null, new object[] {orgs, lambdaExpr});
var dest = parameterizedSelectMethod.Invoke(null, new object[] {members, lambdaExpr});
var errorList = parameterizedExceptMethod.Invoke(null, new[] {source, dest});
var errorListArray = toArrayMethod.MakeGenericMethod(property.PropertyType).Invoke(null, new[] {errorList});
var failedTests = string.Join("\n", ((IEnumerable)errorListArray).Cast<object>().Select(x => x.ToString()));
Assert.IsTrue(0 == failedTests.Length, $"The following {property.Name} are not contained in the source: \n{failedTests}");
}
}

string parsing in linq query and use anonymous type

i have many objects like this:
var obj4 = new Data { name = "person", date = DateTime.Now.AddDays(1), data = "pr-214-2-20151224-word2-word3" };
i want to write a linq query that result be this:
result=[name=person, date=04/09/2016 12:00:00 AM, data=[2,3]]//2 and 3 are the numbers after word
so far i tried this:
var listak=new List<Data>{obj,obj2,obj3,obj4};
var u = listak.OrderByDescending(s => s.date).TakeWhile(s => s.date > DateTime.Now).Select(s=>new
{
name=s.name,
date=s.date,
data=s.data.Split(new []{"-"},StringSplitOptions.None).Select(m=>new
{
word = m.Where(c=>m.StartsWith("word")).Select(c=>m.Remove(0,4))//this line is incorrect, i dont know how to correct this part
})
});
but i cant get what i want, how can i achieve that?
You can try this :
var obj4 = new { name = "person", date = DateTime.Now.AddDays(1), data = "pr-214-2-20151224-word2-word3" };
var listak = new[] { obj4 }.ToList();
var u = listak.OrderByDescending(s => s.date).TakeWhile(s => s.date > DateTime.Now).Select(s => new
{
name = s.name,
date = s.date,
data = s.data.Split(new[] { "-" }, StringSplitOptions.None).Where(c => c.StartsWith("word")).Select(m => m.Remove(0, 4))
});
Console.Write(u);

default values for IEnumerable collection when length is zero

I have a IEnumerable collection:
public IEnumerable<Config> getConfig(string CID, string name)
{
var raw = db.ap_GetInfo(CID, name);
foreach (var item in raw.ToList().Where(x => x.Name!= null))
{
var x = raw.Count();
yield return new Config
{
Name = item.Name.ToString(),
Value = item.Value.ToString(),
};
}
}
The problem I am facing is that if this return a length of zero I am then unable to set the attributes to something else, If I have a response of length 1 the attributes are set from the database, however length zero I want to set a dfault value for Name and Value.
A LINQ solution - this returns the default if there are no items in the enumerable using DefaultIfEmpty:
public IEnumerable<Config> GetConfig(string CID, string name)
{
return db.ap_GetInfo(CID, name)
.Where(x => !string.IsNullOrEmpty(x.Name))
.Select(x => new Config
{
Name = x.Name.ToString(),
Value = x.Value.ToString(),
})
.DefaultIfEmpty(new Config
{
Name = "DefaultName",
Value = "DefaultValue"
});
}
If I understood your question correctly, you want to replace the case
0 results
with
1 result with a default value.
If that is correct, the easiest way is to fix this in the calling function:
var result = getConfig(...).ToList();
if (!result.Any())
{
result = new[] {new Config {Name = "DefaultName", Value = "DefaultValue"}};
}
Obviously, you can wrap this in a new function:
public IEnumerable<ClubConfig> getConfigOrDefault(string CID, string name)
{
var result = getConfig(CID, name).ToList();
if (result.Any())
return result;
else
return new[] {new Config {Name = "DefaultName", Value = "DefaultValue"}};
}
To check if your query did return any elements use Any-method.
public IEnumerable<ClubConfig> getConfig(string CID, string name)
{
var raw = db.ap_GetInfo(CID, name);
if (!raw.Any()) return new[] {
ClubConfig
{
Name = "defaultName",
Value = "defaultValue"
}};
foreach (var item in raw.Where(x => !string.IsNullOrEmpty(x.Name))
{
yield return new ClubConfig
{
Name = item.Name.ToString(),
Value = item.Value.ToString(),
};
}
}
EDIT: You can also omit the ToList from your input.
You can do this using LINQ and can maintain lazy evaluation of the IEnumerable result as follows:
public IEnumerable<ClubConfig> getConfig(string CID, string name)
{
var raw = db.ap_GetInfo(CID, name);
return raw.Where(x => !string.IsNullOrEmpty(x.Name))
.Select(item => new ClubConfig
{
Name = item.Name.ToString(),
Value = item.Value.ToString(),
})
.DefaultIfEmpty(new ClubConfig { Name = "n", Value="v" });
}

LINQ: ...Where(x => x.Contains(string that start with "foo"))

Given a collection of the following class:
public class Post
{
...
public IList<string> Tags { get; set; }
}
Is there an easy way to get all Posts that contain a tag starting with "foo" using LINQ?
var posts = new List<Post>
{
new Post { Tags = new[] { "fooTag", "tag" }},
new Post { Tags = new[] { "barTag", "anyTag" }},
new Post { Tags = new[] { "someTag", "fooBarTag" }}
};
var postsWithFooTag = posts.Where(x => [some fancy LINQ query here]);
postsWithFooTag should now contain items 1 and 3 of posts.
Use string's StartsWith
var postsWithFooTag = posts.Where(x => x.Tags.Any(y => y.StartsWith("foo")));
x.Any will check if any element matches some condition. StartsWith checks if the element starts with a certain string.
The above returned:
new Post { Tags = new[] { "fooTag", "tag" }},
new Post { Tags = new[] { "someTag", "fooBarTag" }}
To make it case insensitive use StringComparison.OrdinalIgnoreCase.
var postsWithFooTag = posts.Where(x => x.Tags.Any(y => y.StartsWith("FoO", StringComparison.OrdinalIgnoreCase)));
Returns:
new Post { Tags = new[] { "fooTag", "tag" }},
new Post { Tags = new[] { "someTag", "fooBarTag" }}
while StartsWith("FoO") returns no results.
Try this:
var postsWithFooTag = posts.Where(x => x.Tags.Any(y => y.StartsWith("foo")))
I believe this will work for what you're trying to do.
posts.Where(p => p.Tags.Any(t => t.StartsWith("foo")))
var tag = "foo";
var postsWithFooTag =
posts.Where( p=> p.Tags.Any( t => t.StartsWith(tag)));
Try x => x.Tags.Any(tag => tag.StartsWith("foo"))

Categories

Resources