Break array into 'tables' with ruby - arrays

I can't seem to get this down, right.
I have a big list of words in an array. I want these words to appear in 8 'tables', each 14 rows by 9 columns, with words running down each column of the table.
So I can get as far as columns = words.each_slice(14) and then later tables = columns.each_slice(9) but from there i'm not sure. I feel like I should make a hash and append the first n item of each column to an array, and then maybe join them with a tab delimiter.
My destination is a spreadsheet, so maybe outputting to CSV would make sense? I'm just not sure how to have it grouped into separate 'tables' (instead of just 9 columns with lots of rows and no separation) but maybe all it takes is a csv line with all blanks?
Anyway, any input or insight would be welcome.

This will do what you ask
You don't say anything about the output format you want, so I've just surrounded each word with quotes, joined them with commas and put a blank line between tables.
My "words" are just the numbers 1 to 200.
words = (1 .. 200).map { |v| '%03d' % v }
words.each_slice(14).each_slice(9) do |table|
(0 ... table[0].size).each do |i|
row = table.map { |column| column[i] }
row.pop if row[-1].nil?
puts row.map { |cell| %<"#{cell}"> }.join ','
end
puts ''
end
output
"001","015","029","043","057","071","085","099","113"
"002","016","030","044","058","072","086","100","114"
"003","017","031","045","059","073","087","101","115"
"004","018","032","046","060","074","088","102","116"
"005","019","033","047","061","075","089","103","117"
"006","020","034","048","062","076","090","104","118"
"007","021","035","049","063","077","091","105","119"
"008","022","036","050","064","078","092","106","120"
"009","023","037","051","065","079","093","107","121"
"010","024","038","052","066","080","094","108","122"
"011","025","039","053","067","081","095","109","123"
"012","026","040","054","068","082","096","110","124"
"013","027","041","055","069","083","097","111","125"
"014","028","042","056","070","084","098","112","126"
"127","141","155","169","183","197"
"128","142","156","170","184","198"
"129","143","157","171","185","199"
"130","144","158","172","186","200"
"131","145","159","173","187"
"132","146","160","174","188"
"133","147","161","175","189"
"134","148","162","176","190"
"135","149","163","177","191"
"136","150","164","178","192"
"137","151","165","179","193"
"138","152","166","180","194"
"139","153","167","181","195"
"140","154","168","182","196"

You were on the right track. Here's a solution that writes in CSV format for lists whose length is a multiple of 14*9. You can also create a spreadsheet directly with the appropriate gem. I'll post an update which handles any length list shortly.
Note that I think each_slice requires you to include Enumerable for at least pre 2.0 Ruby versions.
(0...14*9*2).each_slice(14).collect.each_slice(9) {|table|
table.transpose.each {|row|
puts row.inspect.delete('[]')} ; puts}
If you need to pad your input array to a multiple of 14*9 so that the transpose works, you can use the following:
def print_csv(array)
mod=array.length%(14*9)
array = array+[nil]*(14*9-mod) if mod>0
array.each_slice(14).collect.each_slice(9) {|table|
table.transpose.each {|row|
puts row.reject(&:nil?)*','} ; puts}
end

Related

Finding specific instance in a list when the list starts with a comma

I'm uploading a spreadsheet and mapping the spreadsheet column headings to those in my database. The email column is the only one that is required. In StringB below, the ,,, simply indicates that a column was skipped/ignored.
The meat of my question is this:
I have a string of text (StringA) comes from a spreadsheet that I need to find in another string of text (StringB) which matches my database (this is not the real values, just made it simple to illustrate my problem so hopefully this is clear).
StringA: YR,MNTH,ANNIVERSARIES,FIRSTNAME,LASTNAME,EMAIL,NOTES
StringB: ,YEAR,,MONTH,LastName,Email,Comments <-- this list is dynamic
MNTH and MONTH are intentionally different;
excelColumnList = 'YR,MNTH,ANNIV,FIRST NAME,LAST NAME,EMAIL,NOTES';
mappedColumnList= ',YEAR,,MONTH,,First Name,Last Name,Email,COMMENTS';
mappedColumn= 'Last Name';
local.index = ListFindNoCase(mappedColumnList, mappedColumn,',', true);
local.returnValue = "";
if ( local.index > 0 )
local.returnValue = ListGetAt(excelColumnList, local.index);
writedump(local.returnValue); // dumps "EMAIL" which is wrong
The problem I'm having is the index returned when StringB starts with a , returns the wrong index value which affects the mapping later. If StringB starts with a word, the process works perfectly. Is there a better way to to get the index when StringB starts with a ,?
I also tried using listtoarray and then arraytolist to clean it up but the index is still off and I cannot reliably just add +1 to the index to identify the correct item in the list.
On the other hand, I was considering this mappedColumnList = right(mappedColumnList,len(mappedColumnList)-1) to remove the leading , which still throws my index values off BUT I could account for that by adding 1 to the index and this appears to be reliably at first glance. Just concerned this is a sort of hack.
Any advice?
https://cfdocs.org/listfindnocase
Here is a cfgist: https://trycf.com/gist/4b087b40ae4cb4499c2b0ddf0727541b/lucee5?theme=monokai
UPDATED
I accepted the answer using EDIT #1. I also added a comment here: Finding specific instance in a list when the list starts with a comma
Identify and strip the "," off the list if it is the first character.
EDIT: Changed to a while loop to identify multiple leading ","s.
Try:
while(left(mappedColumnList,1) == ",") {
mappedColumnList = right( mappedColumnList,(len(mappedColumnList)-1) ) ;
}
https://trycf.com/gist/64287c72d5f54e1da294cc2c10b5ad86/acf2016?theme=monokai
EDIT 2: Or even better, if you don't mind dropping back into Java (and a little Regex), you can skip the loop completely. Super efficient.
mappedColumnList = mappedColumnList.replaceall("^(,*)","") ;
And then drop the while loop completely.
https://trycf.com/gist/346a005cdb72b844a83ca21eacb85035/acf2016?theme=monokai
<cfscript>
excelColumnList = 'YR,MNTH,ANNIV,FIRST NAME,LAST NAME,EMAIL,NOTES';
mappedColumnList= ',,,YEAR,MONTH,,First Name,Last Name,Email,COMMENTS';
mappedColumn= 'Last Name';
mappedColumnList = mappedColumnList.replaceall("^(,*)","") ;
local.index = ListFindNoCase(mappedColumnList, mappedColumn,',', true);
local.returnValue = ListGetAt(excelColumnList,local.index,",",true) ;
writeDump(local.returnValue);
</cfscript>
Explanation of the Regex ^(,*):
^ = Start at the beginning of the string.
() = Capture this group of characters
,* = A literal comma and all consecutive repeats.
So ^(,*) says, start at the beginning of the string and capture all consecutive commas until reaching the next non-matched character. Then the replaceall() just replaces that set of matched characters with an empty string.
EDIT 3: I fixed a typo in my original answer. I was only using one list.
writeOutput(arraytoList(listtoArray(mappedColumnList))) will get rid of your leading commas, but this is because it will drop empty elements before it becomes an array. This throws your indexing off because you have one empty element in your original mappedColumnList string. The later string functions will both read and index that empty element. So, to keep your indexes working like you see to, you'll either need to make sure that your Excel and db columns are always in the same order or you'll have to create some sort of mapping for each of the column names and then perform the ListGetAt() on the string you need to use.
By default many CF list functions ignore empty elements. A flag was added to these function so that you could disable this behavior. If you have string ,,1,2,3 by default listToArray would consider that 3 elements but listToArray(listVar, ",", true) will return 5 with first two as empty strings. ListGetAt has the same "includeEmptyValues" flag so your code should work consistently when that is set to true.

How to create a new group of character array in matlab?

I have data stored in text files.The data is in 'cell array of string' after read it using textscan and contains various of colour name. Below is the content of my data:
name of colour
'lavender'
'lavenderblush'
'lemonchiffon'
'lightblue'
'lightcoral'
'lightcyan'
I want to create new array to group all color characters into the main color only (red, blue, orange, brown,etc).
I am really struggling to solve this problem. Thank you in advance for any help.
load_data = fopen('result.txt', 'r');
C = textscan(load_data, ' %s ');
fclose(load_data);
name = C{1,1};
group = char(name)
if group{:,1} == lavender
fprintf('purple');
else
fprintf('nothing');
end
This is my code but if I run this, always get error
Cell contents reference from a non-cell array object.
From your comment above, I assume you're having trouble with your code, not actually with the sorting of colors, or how to categorize them.
group is not a Cell array, it's a Char array. So in order to access its values you should use group(:,1) instead of group{:,1}
Remember that the number of columns in group is the number of characters in that line, normalized to the number of characters of the largest string in that set. So 2 issues here:
You can't use group(:,1), as it will get the first character of all the strings in that array. You should get the entire line for that string group(1,:). NB: I say string for simplicity, it's actually a char array.
'lavender' has only 10 characters, but it will have 15, as per the largest string. So the string comparison doesn't quite work, unless you add the extra blank spaces to compensate
You can try out the code below:
load_data = fopen('result.txt', 'r');
C = textscan(load_data, ' %s ');
fclose(load_data);
name = C{1,1};
if name{1,:} == '''lavender'''
fprintf('purple');
else
fprintf('nothing');
end
I assume that your TXT file actually has the string 'lavender', in this case I used character escape '''lavender'''.
The error you are getting is fairly clear. Once you have called char(name), you are working with a regular character array (i.e. a string) and no longer a cell array. Braces (i.e. {}) are used to index cell arrays so you should instead be using parentheses (i.e. ()). Note this actually happens as soon as you index using {} so C{1,1} would actually return a string already and the char line is thus redundant:
if group(:,1) == lavender
I suspect you actually want something more like
name = C{1};
group = char(name) %this line is redundant because C{1} already extracts a string
if strcmp(group,lavender)
fprintf('purple');
else
fprintf('nothing');
end
but it's impossible to say since you did not define the variable lavender.
I would also question what data structure you intend to use. I'm going to assume you actually have some way of categorizing the colours? I'm going to assume this is manual in which case I would suggest converting your txt file to a csv file and putting your manual colour categories as a second column but I'll leave the implementation details to you.
Lets say you have 3 colour categories for now, 'purple', 'blue', and 'orange', my suggestion is to use a logical matrix that has 3 columns (1 per colour category) and n rows where n is the number of rows in your text file (i.e. the number of colours you need to categorize).
Now I'm going to assume you have some sort of mapping function that can categorize your colours so map('lavender') returns 'purple' and map('lightcyan') returns 'blue'
First we should make a cell array of categories that we can use to map the category string to its column number:
categories = {'purple'
'blue'
'orange'}
and the result will go in the logical matrix categorized
load_data = fopen('result.txt', 'r');
C = textscan(load_data, ' %s ');
fclose(load_data);
n = numel(C);
categories = {'purple'
'blue'
'orange'};
categorized = false(n, numel(categories)); %preallocation
for row = 1:n
colour = C{row};
category = map(colour); %you need to implement this map function yourself.
categorized(row, strcmp(category, categories)) = true;
end

Keeping values of cell arrays when exporting to Excel

I have a cell array. Some of the elements in this cell array contains zeros as the first character and the whole element is only numbers (double) as well. When exporting these to Excel (which I prefer), the zeros are deleted and converting it to a number.
Let's take an example to illustrate my problem. I have a cell array with 10 elements:
NodeID = {'0000006';
'0000011';
'000011R';
'000016R';
'000021R';
'B276_2';
'EB 7.55';
'EB2521';
'EllebaekOPlB1';
'EllebaekOplB10'};
The first two elements contains zeros until the number 6 and 11, respectively. Unlike the third element and so forth, where letters are involved. So when exporting NodeID to Excel, it returns this in a column (I use writetable command by the way):
6
11
000011R
000016R
000021R
B276_2
EB 7.55'
EB2521
EllebaekOPlB1
EllebaekOplB10
Notice the removal of zeros for the first two elements. Now I know that in Excel, it will keep all the content with the addition of a quote symbol ' in front of the cell, eg. '0000006 for the first element.
I have searched in many places to find a solution to this. But is there a good way to avoid this from happening? Either by somehow adding an extra ยด or some other magical trick which I have not seen?
Thank you in advance!
One alternative (if your values are in a cell, as you say they are):
filename = 'NodeID.xlsx';
NodeID2 = cellfun(#(C) ['''',C], NodeID,'UniformOutput', false)
xlswrite(filename, NodeID2)
This gives you:
NodeID2 =
''0000006'
''0000011'
''000011R'
''000016R'
''000021R'
''B276_2'
''EB 7.55'
''EB2521'
''EllebaekOPlB1'
''EllebaekOplB10'
And an Excel file looking like this:
The cellfun line is equivalent to:
for ii = 1:numel(NodeID)
NodeID2{ii,1} = ['''', NodeID{ii}];
end
The part ['''', NodeID{ii}] inserts a single quotation mark in front for the string. Relevant answer.

Apply a string value to several positions of a cell array

I am trying to create a string array which will be fed with string values read from a text file this way:
labels = textread(file_name, '%s');
Basically, for each string in each line of the text file file_name I want to put this string in 10 positions of a final string array, which will be later saved in another text file.
What I do in my code is, for each string in file_name I put this string in 10 positions of a temporary cell array and then concatenate this array with a final array this way:
final_vector='';
for i=1:size(labels)
temp_vector=cell(1,10);
temp_vector{1:10}=labels{i};
final_vector=horzcat(final_vector,temp_vector);
end
But when I run the code the following error appears:
The right hand side of this assignment has too few values to satisfy the left hand side.
Error in my_example_code (line 16)
temp_vector{1:10}=labels{i};
I am too rookie in cell strings in matlab and I don't really know what is happening. Do you know what is happening or even have a better solution to my problem?
Use deal and put the left hand side in square brackets:
labels{1} = 'Hello World!'
temp_vector = cell(10,1)
[temp_vector{1:10}] = deal(labels{1});
This works because deal can distribute one value to multiple outputs [a,b,c,...]. temp_vector{1:10} alone creates a comma-separated list and putting them into [] creates the output array [temp_vector{1}, temp_vector{2}, ...] which can then be populated by deal.
It is happening because you want to distribute one value to 10 cells - but Matlab is expecting that you like to assign 10 values to 10 cells. So an alternative approach, maybe more logic, but slower, would be:
n = 10;
temp_vector(1:n) = repmat(labels(1),n,1);
I also found another solution
final_vector='';
for i=1:size(labels)
temp_vector=cell(1,10);
temp_vector(:,1:10)=cellstr(labels{i});
final_vector=horzcat(final_vector,temp_vector);
end

google spreadsheet: join arrays using function NOT CODE

I have one array
1
2
3
another array
4
5
6
How do I use one function to join the 2 arrays?
1
2
3
4
5
6
Let's say your arrays are:
A1:A3 = {1;2;3} and B1:B3 = {4;5;6}
Write somewhere: ={A1:A3;B1:B3}
Semicolons ; are used to separate rows, and commas , are for columns.
Here is the documentation: Using arrays in Google Sheets
TRANSPOSE() //takes matrix as argument and returns transposed matrix
SPLIT() //breaks apart a string based on a delimiter character (char(13) here)
ARRAYFORMULA() //applies the formula within to array vs cell
CONCATENATE() //joins each cell in each range with a char(13), then the next cell...then the next matrix of cells (to which the first process also happens)
//note char(13) is a carriage return, i will call CR for ease
so if you have matrix A : 1, 2, 3
and matrix B : 4, 5, 6
the steps would look like this:
TRANSPOSE(SPLIT(ARRAYFORMULA(CONCATENATE("1CR2CR3CR" ; "4CR5CR6CR")), CR))
TRANSPOSE(SPLIT("1CR2CR3CR4CR5CR6CR", "CR"))
TRANSPOSE({"1","2","3","4","5","6"})
finally:
1
2
3
4
5
6
=FLATTEN(A1:A3,B1:B3) should do the trick
=filter({A1:A;B1:B}, {A1:A;B1:B}<>"")
I'm noticing this is an older question, so this might not have existed when previous responses were written.
You're looking for the JOIN() formula.
(Documentation link)
Sample Usage
JOIN(" and-a ",{1,2,"1 2 3 4"})
JOIN(",",{1,2,3},{4;5;6})
JOIN("-",A1:A100)
Syntax
JOIN(delimiter, value_or_array1, [value_or_array2, ...])
delimiter - The character or string to place between each concatenated value.
delimiter may be specified as blank, e.g. JOIN(,{1,2,3}).
value_or_array1 - The value or values to be appended using delimiter.
value_or_array2, ... - [ OPTIONAL ] - Additional value or array to be appended using delimiter.
Given the information provided by Mr.Monshaw, I figured this out for you.
Given values "1,2,3" are in cells A1:A3 of a spreadsheet, and values "4,5,6,7" are in cells B1:B4. Here is the formula to use:
=TRANSPOSE(SPLIT(ARRAYFORMULA(CONCATENATE(concat(A1:A3,",");concat(B1:B4,","))),","))
Explanation.
The concat formula creates a string of the values with the indicated separator, ",". So concat(A1:A3,",") results in "1,2,3".
The Concatenate combines the values of the specified array as one string. So CONCATENATE(concat(A1:A3,",");concat(B1:B4,",")) results in "1,2,3,4,5,6,7"
The Split function here is used to identify the "," as a deliminator and not a value in the array. The Transpose will cause the results to display in a column instead of in a row.
I found this even easier...
=ARRAYFORMULA({A:A;B:B})

Resources