It would be more efficient and more maintainable if you define a class that you could store your data for each person rather than storing it in an object which in that case it would need unnecessary boxing and unboxing every time you search through your list. having that said you could search in your data structure like this:
var result = lstData.Find(x => x["Name"] == (object)"John" && x["School"] == (object)"Waterloo");
var age = (int)result["Age"]; //20
Edit
If the goal is to get properties as an anonymous list without knowing the property names at compilation time it's not possible. the anonymous type in C# needs the property name first hand or at least property access when you define it. an alternative way to solve this problem would be to use another dictionary so you could map the original one into:
var summayList = lstData.Select(x => new Dictionary<string, object>()
{
{ "Name", x["Name"] },
{ "School", x["School"] }
});
var john = summayList.Single(x => x["Name"] == (object)"John");
if the number of properties is unknown in addition to their names, you could use a method that return a list of dictionaries given unknown number of property names:
public static IEnumerable<IDictionary<string, object>> GetProps(List<IDictionary<string, object>> list, params string[] props)
{
return list.Select(x => new Dictionary<string, object>(
props.Select(p => new KeyValuePair<string, object>(p, x[p]))));
}
Usage
var result = GetProps(lstData, "Name", "School");
var goli = result.Single(x => x["Name"] == (object)"Goli");