Related
I have a Hash that looks like:
day = {
:morning => true,
:afternoon => false,
:evening => true,
:night => true
}
I'd like to get the percent of values that == true
I'm current doing
sum = day.count.to_f
adherence_percent = (day.select{|k, v| v == true }.count / sum) * 100
:> 66.6666666
Is there a more elegant/efficient way I can do this?
You can do this using count and last
day.count(&:last).fdiv(day.size)
Demonstration
If you know there are only true/false values in there
day.values.count(&:itself)
otherwise
day.values.count { |item| item == true }
usage:
sum = day.count.to_f
adherence_percent = day.values.count(&:itself) / sum * 100
I am trying to solve a problem.
Problem :
You are given a sequence of N balls in 4 colors: red, green, yellow and blue. The sequence is full of colors if and only if all of the following conditions are true:
There are as many red balls as green balls.
There are as many yellow balls as blue balls.
Difference between the number of red balls and green balls in every prefix of the sequence is at most 1.
Difference between the number of yellow balls and blue balls in every prefix of the sequence is at most 1.
Your task is to write a program, which for a given sequence prints True if it is full of colors, otherwise it prints False.
My solution : for each string, i am generating all possible prefixes and suffixes to validate the condition number 3 and 4. But it is taking more time.
instead of generating prefix and validating conditions every time, we can iterate over the string and validate the condition. I want to break out of loop when condition is not met. I am not able to get that in functional style. Can someone help me how to achieve it.
My solution :
object Test {
def main(args: Array[String]) {
def isValidSequence(str: String) = {
def isValidCondition(ch1:Char, ch2:Char, m:Map[Char, Int]):Boolean = m.getOrElse(ch1, 0) - m.getOrElse(ch2, 0) > 1
def groupByChars(s:String) = s.groupBy(ch => ch).map(x => (x._1, x._2.length))
def isValidPrefix(s:String):Boolean = (1 to s.length).exists(x => isValidCondition('R', 'G', groupByChars(s.take(x))))
val x = groupByChars(str)
lazy val cond1 = x.get('R') == x.get('G')
lazy val cond2 = x.get('B') == x.get('Y')
lazy val cond3 = isValidPrefix(str)
lazy val cond4 = isValidPrefix(str.reverse)
cond1 && cond2 && !cond3 && !cond4
}
def printBoolValue(b:Boolean) = if(b) println("True") else println("False")
val in = io.Source.stdin.getLines()
val inSize = in.take(1).next().toInt
val strs = in.take(inSize)
strs.map(isValidSequence(_)).foreach(printBoolValue)
}
}
As another answer, here's a more straightforward solution, that does short-circuit the differences check.
val valid = List("RGYBRGYB")
val invalid = List("RGYBR", "RGYBY", "RGYBY", "RGYYB")
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) true
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond1 = s.count('R'==) == s.count('G'==)
lazy val cond2 = s.count('Y'==) == s.count('B'==)
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond1 && cond2 && cond3 && cond4
}
valid.forall(checkBalls(_)) //> res0: Boolean = true
invalid.forall(!checkBalls(_)) //> res1: Boolean = true
EDIT: as an optimisation, we can do cond1 as part of cond3 (and cond2 as part of cond4). There are equal numbers of each if and only if the count is 0 at the end of the string. We can check that in differences and return true only if that's the case. So that gives
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) (count == 0) // <- this line changed
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond3 && cond4
}
which passes the tests just like the previous version. It could be made slightly faster by doing the R/G and Y/B checks in one call to differences, but that's looking a bit overspecialised.
Here is a solution using streams if you need.
code :-
object RGYB extends App {
val validPattern = List(
"RG","RYBG","RYGB","RBGY",
"GR","GYBR","GYRB","GBRY",
"YB","YRGB","YRBG","YGRB",
"BY","BRGY","BRYG","BGYR"
)
val pattern ="RGRG"
pattern.sliding(4).foreach { x1 =>
val count = validPattern.filter { p1 => {
x1.equalsIgnoreCase(p1)
}
}.size
if(count<1)
{
x1.sliding(2).foreach {
x2=>
val counter = validPattern.filter { p2 => {
x2.equalsIgnoreCase(p2)
}
}.size
if(counter<1)
{
println("false !! not valid due to "+x2);
System.exit(0)
}
}
println("false !! not valid due to "+x1);
System.exit(0)
}
}
println("True !!"+pattern+" Is a valid string pattern")
}
So, the trick is to check the longest prefix first. If that fails, we're done. Otherwise, we take the next longest prefix and recurse. If we get to the empty string, it passed for all prefixes, and therefore it's valid.
def isValidPrefix(s: String): Boolean =
if (s.length == 0)
true
else if (!isValidCondition('R', 'G', groupByChars(s)))
false
else isValidPrefix(s.init)
How to write this LINQ expression in another .SelectMany() form ?
var result =
from a in numbersA
where a < 3
from b in numbersB
where b < 5
select new { a, b };
?
var result = numbersA.Where(x => x < 3).Select.. ?
This is a rough translation of what the compiler would do:
var result = numbersA.Where(a => a < 3)
.SelectMany(a => numbersB, (a, b) => new { a, b })
.Where(z => z.b < 5)
.Select(z => new { z.a, z.b });
Now you can write this more efficiently as:
var result = numbersA.Where(a => a < 3)
.SelectMany(a => numbersB.Where(b => b < 5),
(a, b) => new { a, b });
... but that's not what the compiler would do. It's not clear whether your aim is to see what the compiler does, or to just write a query.
Something like
var result = numbersA.Where(a => a < 3).SelectMany(a =>
numbersB.Where(b => b < 5).Select(b => new { a, b }));
Note that it maybe be more efficient to filter numbersB once only:
var filteredB = numbersB.Where(b => b < 5).ToArray();
var result = numbersA.Where(a => a < 3).SelectMany(a =>
filteredB.Select(b => new { a, b }));
I have two collections that I want to intersect, and perform a sum operation on matching elements.
For example the collections are (in pseudo code):
col1 = { {"A", 5}, {"B", 3}, {"C", 2} }
col2 = { {"B", 1}, {"C", 8}, {"D", 6} }
and the desired result is:
intersection = { {"B", 4}, {"C", 10} }
I know how to use an IEqualityComparer to match the elements on their name, but how to sum the values while doing the intersection?
EDIT:
The starting collections haven't two items with the same name.
Let's say your input data looks like this:
IEnumerable<Tuple<string, int>> firstSequence = ..., secondSequence = ...;
If the strings are unique in each sequence (i.e there can be no more than a single {"A", XXX} in either sequence) you can join like this:
var query = from tuple1 in firstSequence
join tuple2 in secondSequence on tuple1.Item1 equals tuple2.Item1
select Tuple.Create(tuple1.Item1, tuple1.Item2 + tuple2.Item2);
You might also want to consider using a group by, which would be more appropriate if this uniqueness doesn't hold:
var query = from tuple in firstSequence.Concat(secondSequence)
group tuple.Item2 by tuple.Item1 into g
select Tuple.Create(g.Key, g.Sum());
If neither is what you want, please clarify your requirements more precisely.
EDIT: After your clarification that these are dictionaries - your existing solution is perfectly fine. Here's another alternative with join:
var joined = from kvp1 in dict1
join kvp2 in dict2 on kvp1.Key equals kvp2.Key
select new { kvp1.Key, Value = kvp1.Value + kvp2.Value };
var result = joined.ToDictionary(t => t.Key, t => t.Value);
or in fluent syntax:
var result = dict1.Join(dict2,
kvp => kvp.Key,
kvp => kvp.Key,
(kvp1, kvp2) => new { kvp1.Key, Value = kvp1.Value + kvp2.Value })
.ToDictionary(a => a.Key, a => a.Value);
This will give the result, but there are some caveats. It does an union of the two collections and then it groups them by letter. So if, for example, col1 contained two A elements, it would sum them together and, because now they are 2 A, it would return them.
var col1 = new[] { new { L = "A", N = 5 }, new { L = "B", N = 3 }, new { L = "C", N = 2 } };
var col2 = new[] { new { L = "B", N = 1 }, new { L = "C", N = 8 }, new { L = "D", N = 6 } };
var res = col1.Concat(col2)
.GroupBy(p => p.L)
.Where(p => p.Count() > 1)
.Select(p => new { L = p.Key, N = p.Sum(q => q.N) })
.ToArray();
The best I came up with until now is (my collections are actually Dictionary<string, int> instances):
var intersectingKeys = col1.Keys.Intersect(col2.Keys);
var intersection = intersectingKeys
.ToDictionary(key => key, key => col1[key] + col2[key]);
I'm not sure if it will perform well, at least is it readable.
If your intersection algorithm will result in anonymous type, i.e. ...Select(new { Key = key, Value = value}) then you can easily sum it
result.Sum(e => e.Value);
If you want to sum the "while" doing the intersection, add the value to the accumulator value when adding to the result set.
This question is the second part of another question of mine, but this time focused on LINQ-to-SQL.
I have 2 tables, one containing meter IDs, and another containing measurements for some of the meters in the first table. This is the table structure:
MeterConfig:
MeterID (int)
MeterNumber (char[16])
Type (char[25])
Readings:
MeterID (int)
Date (datetime)
Value (numeric(18,6))
I need to get the last reading (and its date) from a given period for each meter, as well as the meter number. The date a meter was last read can differ from one to the other. Some meters might not have any readings in that period; in that case, they must be ignored.
I managed to do this, but I'm not exactly thrilled by how I did it. Is there a more efficient way of doing it, without having to do almost the same subquery twice?
(from cfg in MeterConfigs
join r in Readings on cfg.MeterID equals r.MeterID
where
r.Date >= startDate && r.Date <= endDate
select new
{
Number = cfg.MeterNumber,
ReadingDate =
(
from r in Readings
where cfg.MeterID == r.MeterID && r.Date >= startDate && r.Date <= endDate
orderby r.Date descending
select r.Date
).Take(1),
Value =
(
from r in Readings
where cfg.MeterID == r.MeterID && r.Date >= startDate && r.Date <= endDate
orderby r.Date descending
select r.Value
).Take(1)
})
.GroupBy(x => x.Number)
.Select(x => x.First());
Here is one possible solution:
MeterConfigs
.Join(
Readings.Where(x => x.Date >= startDate && x.Date <= endDate),
x => x.MeterID,
x => x.MeterID,
(o,i) => new { MeterNumber = o.MeterNumber, Date = i.Date, Value = i.Value }
).GroupBy(x => x.MeterNumber)
.Select(x => {
var lastReading = x.OrderByDescending(y => y.Date).FirstOrDefault();
return new {
Number = x.Key,
ReadingDate = lastReading.Date,
Value = lastReading.Value
};
}
)
If you have linqpad installed you can compare the results of this query to your current solution with some dummy data I created:
var MeterConfigs = new [] {
new { MeterID = 1, MeterNumber = "123", Type = "foo" },
new { MeterID = 2, MeterNumber = "456", Type = "bar" },
new { MeterID = 3, MeterNumber = "789", Type = "foo" },
new { MeterID = 4, MeterNumber = "101", Type = "bar" },
};
var Readings = new [] {
new { MeterID = 1, Date = new DateTime(2010, 10, 21), Value = 12.3 },
new { MeterID = 1, Date = new DateTime(2010, 10, 20), Value = 4.3 },
new { MeterID = 1, Date = new DateTime(2010, 10, 19), Value = 56.2 },
new { MeterID = 1, Date = new DateTime(2010, 10, 5), Value = 1.4 },
new { MeterID = 2, Date = new DateTime(2010, 10, 20), Value = 8.2 },
new { MeterID = 3, Date = new DateTime(2010, 10, 21), Value = 34.7 },
new { MeterID = 3, Date = new DateTime(2010, 10, 20), Value = 2.9 },
};
var startDate = new DateTime(2010, 10, 1);
var endDate = new DateTime(2010, 10, 21);
MeterConfigs
.Join(
Readings.Where(x => x.Date >= startDate && x.Date <= endDate),
x => x.MeterID,
x => x.MeterID,
(o,i) => new { MeterNumber = o.MeterNumber, Date = i.Date, Value = i.Value }
).GroupBy(x => x.MeterNumber)
.Select(x => {
var lastReading = x.OrderByDescending(y => y.Date).FirstOrDefault();
return new {
Number = x.Key,
ReadingDate = lastReading.Date,
Value = lastReading.Value
};
}
).Dump();
(from cfg in MeterConfigs
join r in Readings on cfg.MeterID equals r.MeterID
where
r.Date >= startDate && r.Date <= endDate
select new
{
Number = cfg.MeterNumber,
ReadingDate =
(
from r2 in Readings
where cfg.MeterID == r2.MeterID && r2.Date >= startDate && r2.Date <= endDate
orderby r2.Date descending
select r2.Date
).Take(1),
Value =
(
from r2 in Readings
where cfg.MeterID == r2.MeterID && r2.Date >= startDate && r2.Date <= endDate
orderby r2.Date descending
select r2.Value
).Take(1)
})
.GroupBy(x => x.Number)
.Select(x => x.First()).Dump();
Returns:
Number ReadingDate Value
123 10/21/2010 12:00:00 AM 12.3
456 10/20/2010 12:00:00 AM 8.2
789 10/21/2010 12:00:00 AM 34.7
Versus:
Number ReadingDate Value
123 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/21/2010 12:00:00 AM 12.3
456 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/20/2010 12:00:00 AM 8.2
789 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/21/2010 12:00:00 AM 34.7