Count number of occurrences based on 2 conditions or Regexp - regex

How can I get the number of occurrences for some range based on
A regular expression
2+ conditions; let's say cells that contain "yes" and / or "no"
What I've got for the moment:
COUNTIF(B5:O5; "*yes*")
I tried to use COUNTIF(B5:O5; {"*yes*", "*no*"}) or COUNTIF(B5:O5; "(*yes*)|(*no*)"), but neither of them worked.
Or, how do I count cells that contain some domain names—yahoo.com, hotmail.com, and gmail.com—using regexp? e.g.:
(\W|^)[\w.+\-]{0,25}#(yahoo|hotmail|gmail)\.com(\W|$)

The most pedestrian solution to your problem (tested in Excel and Google Docs) is to simply add the result of several countif formulas:
=COUNTIF(B5:O5, "*yes*") + COUNTIF(B5:O5, "*no*")
This expression will count the total of cells with "yes" or "no". It will double count a cell with "yesno" or "noyes" since it matches both expressions. You could try to take out the doubles with
=COUNTIF(B5:O5, "*yes*") + COUNTIF(B5:O5, "*no*") - COUNTIF(B5:O5, "*no*yes*") - COUNTIF(B5:O5, "*yes*no*")
But that will still get you in trouble with a string like noyesno.
However there is a rather clever trick in Google Docs that may just be a hint of the solution you are looking for:
=COUNTA(QUERY(A1:A9, "select A where A matches '(.*yes.*)|(.*no.*)'"))
The QUERY function is like a mini database thing. In this case it looks at the table in range A1:A9, and selects only elements in column A where the corresponding element in column A matches (in the preg regex sense of the word) the expression that follows - in this case, "anything followed by yes followed by anything, or anything followed by no followed by anything". In a simple example I made, this counts a yesnoyes only once - making it exactly what you were asking for (I think...)
Right now your range B5:O5 is several columns wide, and only one row high; that makes it hard to use the QUERY trick. Something rather less elegant (but that works regardless of the shape of the range) is this:
=countif(arrayformula(isnumber(find("yes",A1:A9))+isnumber(find("no",A1:A9))),">0")
The sum of the isnumber functions acts as an element-wise OR - unfortunately, the regular OR function doesn't seem to work on individual elements of an array. As before, this finds cells that contain either "yes" or "no", and counts the ones that have either of these strings contained within.

This is heavily inspired by Floris's answer. See the comments, in particular. If you TRANSPOSE the row of items to compare against, QUERY works fine for horizontal data as well:
=COUNTA(QUERY(TRANSPOSE(B5:O5), "select * where Col1 matches '.*(yes|no).*'"))
As far as I can tell, Col1 is "special" and case sensitive!

Recently I wanted this functionality in a Google Sheet. I came up with a different solution that also looks very readable.
=COUNTIF(ARRAYFORMULA(REGEXMATCH(O36:R36,"(yes|no)")),TRUE)
Basically what it does is, run REGEXMATCH over a range using ArrayFormula which returns either TRUE or FALSE. And then use COUNTIF to count the occurances of TRUE.

The easiest way NGix found is to create a custom function for these purposes. They added 2 that work perfectly for them and hope it'll help someone too:
/**
* Count if cell value matches any condition ( supports regexp also )
*/
function countCustomMatchOr(data){
var count = 0;
for(var i=0; i < data.length; i++){
for(var j=0; j<data[i].length; j++){
var cell = data[i][j];
for(var k=1;k<arguments.length;k++){
if(typeof arguments[k] == "number"){
if(arguments[k] == cell) count++;
} else if(cell.toString().match(arguments[k])) count++;
}
}
}
return count;
}
And
/**
* Counts value in data range if matches regular expression
*/
function countCustomRegExp(data, reg, flag){
var rows = data.length, count = 0, re = flag?new RegExp(reg, flag):new RegExp(reg);
for( var i = 0; i < rows; i++){
for( var j = 0; j < data[i].length; j++){
if( data[i][j] != "" && data[i][j].toString().match(re) ){
count++;
}
}
}
return count;
}
In order to use them just apply countCustomRegExp(A2:G3;"yes.*") or countCustomMatchOr(A2:G3;"yes";"no")

I've had the same problem, if you don't want to use VBA, try to add a SUM to your formula:
=SUM(COUNTIF(B5:O5; {"*yes*", "*no*"}))

Related

Extracting numbers using Regex in Matlab

I would like to extract integers from strings from a cell array in Matlab. Each string contains 1 or 2 integers formatted as shown below. Each number can be one or two digits. I would like to convert each string to a 1x2 array. If there is only one number in the string, the second column should be -1. If there are two numbers then the first entry should be the first number, and the second entry should be the second number.
'[1, 2]'
'[3]'
'[10, 3]'
'[1, 12]'
'[11, 12]'
Thank you very much!
I have tried a few different methods that did not work out. I think that I need to use regex and am having difficulty finding the proper expression.
You can use str2num to convert well formatted chars (which you appear to have) to the correct arrays/scalars. Then simply pad from the end+1 element to the 2nd element (note this is nothing in the case there's already two elements) with the value -1.
This is most clearly done in a small loop, see the comments for details:
% Set up the input
c = { ...
'[1, 2]'
'[3]'
'[10, 3]'
'[1, 12]'
'[11, 12]'
};
n = cell(size(c)); % Initialise output
for ii = 1:numel(n) % Loop over chars in 'c'
n{ii} = str2num(c{ii}); % convert char to numeric array
n{ii}(end+1:2) = -1; % Extend (if needed) to 2 elements = -1
end
% (Optional) Convert from a cell to an Nx2 array
n = cell2mat(n);
If you really wanted to use regex, you could replace the loop part with something similar:
n = regexp( c, '\d{1,2}', 'match' ); % Match between one and two digits
for ii = 1:numel(n)
n{ii} = str2double(n{ii}); % Convert cellstr of chars to arrays
n{ii}(end+1:2) = -1; % Pad to be at least 2 elements
end
But there are lots of ways to do this without touching regex, for example you could erase the square brackets, split on a comma, and pad with -1 according to whether or not there's a comma in each row. Wrap it all in a much harder to read (vs a loop) cellfun and ta-dah you get a one-liner:
n = cellfun( #(x) [str2double( strsplit( erase(x,{'[',']'}), ',' ) ), -1*ones(1,1-nnz(x==','))], c, 'uni', 0 );
I'd recommend one of the loops for ease of reading and debugging.

problem in using list in game maker studio

I am new to the game maker. I created a list and I want to compare all the data in the list with a specific value. I used the following code:
for(var i=0;i<ds_list_size(lst);i++;)
{
if ds_list_find_value(lst,i)>tmp
ds_list_replace(lst,i,ds_list_find_value(lst,i)-1);
}
and I face the following error:
Push :: Execution Error - Variable Get -1.lst(100001, -1)
at
gml_Object_object0_RightButtonPressed_1 (line 21) - for(var i=0;i
where is my problem?
Thanks all.
if your first for loop i = 0; and when the first entry in the list is smaller than tmp it tries to replace the first place in the list with a not existing one. so you could either check if its the first entry of the list with
if ( i == 0 ) { }
or your could start the for loop from the second entry with
for(var i=1;i<ds_list_size(lst);i++;)
I think the ; at the end of i++; is unnecessary, you only need to use ; in a for-loop as seperator.
GML gives more freedom to common C# rules though (like how there are no brackets needed around an if-condition), so perhaps that's allowed.
Another possibility might be that the index is out of range at ds_list_replace()

How to convert one string to another by successive substitutions of characters?

I'm currently trying to design an algorithm that doing such thing:
I got two strings A and B which consist of lowercase characters 'a'-'z'
and I can modify string A using the following operations:
1. Select two characters 'c1' and 'c2' from the character set ['a'-'z'].
2. Replace all characters 'c1' in string A with character 'c2'.
I need to find the minimum number of operations needed to convert string A to string B when possible.
I have 2 ideas that didn't work
1. Simple range-based for cycle that changes string B and compares it with A.
2. Idea with map<char, int> that does the same.
Right now I'm stuck on unit-testing with such situation : 'ab' is transferable to 'ba' in 3 iterations and 'abc' to 'bca' in 4 iterations.
My algorithm is wrong and I need some fresh ideas or working solution.
Can anyone help with this?
Here is some code that shows minimal RepEx:
int Transform(string& A, string& B)
{
int count = 0;
if(A.size() != B.size()){
return -1;
}
for(int i = A.size() - 1; i >= 0; i--){
if(A[i]!=B[i]){
char rep_elem = A[i];
++count;
replace(A.begin(),A.end(),rep_elem,B[i]);
}
}
if(A != B){
return -1;
}
return count;
}
How can I improve this or I should find another ideas?
First of all, don't worry about string operations. Your problem is algorithmic, not textual. You should somehow analyze your data, and only afterwards print your solution.
Start with building a data structure which tells, for each letter, which letter it should be replaced with. Use an array (or std::map<char, char> — it should conceptually be similar, but have different syntax).
If you discover that you should convert a letter to two different letters — error, conversion impossible. Otherwise, count the number of non-trivial cycles in the conversion graph.
The length of your solution will be the number of letters which shouldn't be replaced by themselves plus the number of cycles.
I think the code to implement this would be too long to be helpful.

Count value in template expression

I want to count a value inside a template expression, in Xtend, without printing it out.
This is my code:
def generateTower(Tower in) {
var counter = 0.0;
'''
One Two Three Four
«FOR line : in.myTable»
«counter» «line.val1» «line.val2» «line.val3»
«counter = counter + 1»
«ENDFOR»
'''
}
So this will generate a table with four columns, whereas the first column is incremented starting at 0.0. The problem is, that «counter = counter + 1» is printed as well. But I want the expression above to just count up, without printing it out.
What could be the best solution to solve this problem?
You could use this simple and readable solution:
«FOR line : in.myTable»
«counter++» «line.val1» «line.val2» «line.val3»
«ENDFOR»
If you insist on the separate increment expression, use a block with null value. This works because the null value is converted to empty string in template expressions (of course you could use "" as well):
«FOR line : in.myTable»
«counter» «line.val1» «line.val2» «line.val3»
«{counter = counter + 1; null}»
«ENDFOR»
Although the first solution is the better. If you require complex logic in a template expression I recommend implementing it by methods not by inline code...
And finally, here is a more OO solution for the problem:
class TowerGenerator {
static val TAB = "\t"
def generateTower(Tower in) {
var counter = 0
'''
One«TAB»Two«TAB»Three«TAB»Four
«FOR line : in.myTable»
«generateLine(line, counter++)»
«ENDFOR»
'''
}
def private generateLine(Line line, int lineNumber) '''
«lineNumber»«TAB»«line.val1»«TAB»«line.val2»«TAB»«line.val3»
'''
}
Xtend is a full-fledged programming language. You can write Java-like expressions and templates. The problem there is that you're inside a triple quote (template), and everything you write there gets outputted. You can count inside the loop, but take into account that you're counting the elements in the in.myTable collection, and this can be obtained using in.myTable.length. So count could be calculated beforehand as in.myTable.length.
«{counter = counter + 1; null}» definitely worked. But as a recommendation, since it is java, writing it as «{counter++; null}» should do the trick as well. It helps because, you may need to modify your code and you can also put it in front as in: ++counter - by putting the operator first, the compiler takes a number, adds one to it before reading the value.

for loop stops when if statement is satisfied

I am trying to count how many times the unique words in a LinkedList appear using this code:
for(int i2 = 0; i2 < a.size(); i2++)
{
word2 = a.get(i2);
for(int j2 = 0; j2 < a.size(); j2++)
{
if(word2 == a.get(j2))
{
counter++;
}
}
System.out.println(word2 + " : " + counter);
counter = 0;
}
But the counter prints out:
Alphabet : 1
Alright : 1
Apple : 1
Alphabet : 1
Alright : 1
Apple : 1
Alphabet : 1
Alright : 1
Apple : 1
There is obviously more than one of the words, but the counter never gets higher then one. I think that the inner for loop is stopping when the if statement is satisfied, but I don't want it to. Any suggestions?
You should use
word2.equals(a.get(j2))
Using "==" you compare the references to those String which are not equal.
And by the way you will print the counter multiple times for the same word if you have repetitions. Let's say you have the word Apple on 2 positions in your list. When you will reach these 2 positions the counter will go up to 2 and you will print (Apple: 2) 2 times
This seems like java to me. You can't compare string with (==) operator.
if(word2.equals(a.get(j2)))
Just a new addition!!!
As suggested by #user3412998 its better to use Sets so that you can avoid duplication.
Now if you need to use ArrayList only then you need to take one element and check if there are multiple copies of that element using loop.The condition can be checked by .equals method if you are using string or my contains method incase you are inserting objects(dont forget to override your equals method in the corresponding class). This is a very tedious process and I do not recommend it.
I believe that the easiest way will to not to add duplicate elements in your array list. to accomplish that look at the code below..
if(!a.contains(word)){
a.add(word);
}
Note word is a String object.
Here I am checking weather the string is already contained in the ArrayList, before insertion.
make a bit of modifications so that you can use it, for arrays, or for directly inputting data etc..
I hope this helps..