This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
augustas656's profile picture

Most efficient way of splitting text into lines by the given width

Started by augustas656, 28 May 2014 - 01:24 PM
augustas656 #1
Posted 28 May 2014 - 03:24 PM
I've made a function that accepts two parameters, but the second one is optional, if you don't put the second one it is replaced with the terminal's screen width. The I use a for loop to have the value of i start from 1 and go towards the text's length, and each loop i increases by the area, so it becomes 1, then 52 etc. The I use string.sub to split where the string's line goes. This method works for me, however, is there any other more efficient way, perhaps with gmatch or gsub?

Regards
Augustas
CometWolf #2
Posted 28 May 2014 - 03:40 PM
Take a look at the function i posted last time you asked about this stuff. Using gsub or gmatch in this case dosen't really make much sense. Based on your explaination, im also somewhat confused by how your current functions actually works, could you just post it instead?
augustas656 #3
Posted 28 May 2014 - 04:18 PM
Ermm, I want to split line not by \n, but by the given area a string of 100 characters and the given area of 30 would split into 4 lines of 30, 30, 30 and 10 characters long in a table. This function doesn't split at \n though.

My line split function (not \n):

function splitToLines(string, area)
local str = {} local line = 1
local area = area or 51
  for i = 1, #string, area do
  str[line] = string:sub(i, i + (area - 1))
  end
return str
end

Regards
Augustas
CometWolf #4
Posted 28 May 2014 - 04:43 PM
Ah i thought your current function was somewhat different. The one you posted wouldn't work properly though, since you don't seem to increase the line variable. It's not really nessacary at all though.


function splitToLines(string, area)
  local str = {}
  for i = 1, #string, area or 51 do
    str[#str+1] = string:sub(i, i + (area - 1))
  end
  return str
end
apemanzilla #5
Posted 28 May 2014 - 05:00 PM
Here is the function I use, which splits a single long string into a numbered table based on the constraint at spaces instead of breaking off words. It does not take into account \n at the moment, but works perfectly for everything else.


local function parseLines(text,constraint)
	local text = text:gsub("\n"," ")
	local words = {}
	local word = ""
	for i = 1, #text do
		if text:sub(i,i) == " " then
			table.insert(words, word)
			word = ""
		else
			word = word .. text:sub(i,i)
		end
	end
	table.insert(words, word)
	local lines = {}
	local line = ""
	for i,v in ipairs(words) do
		if #line + #v + 1 > constraint then
			table.insert(lines, line)
			line = v
		else
			if line ~= "" then
				line = line .. " " .. v
			else
				line = v
			end
		end
	end
	table.insert(lines,line)
	for k,v in pairs(lines) do
		if v == "" then lines[k] = nil end
	end
	return lines
end
Edited on 28 May 2014 - 03:02 PM
CometWolf #6
Posted 28 May 2014 - 05:07 PM
That's uh… unnesacarily computation heavy :P/> Mine also works with \n
For the record, here's the function i use

string.lineFormat = function(text,lineLength,center)
  local tLines = {}
  while #text > 0 do  --splits text into a table containing each line
	local line = text:sub(1,lineLength)
	local newLine = string.find(line.."","\n") --check for new line character
	if newLine then
	  line = line:sub(1,newLine-1)
	  text = text:sub(#line+2,#text)
	elseif #line == lineLength then
	  local endSpace = line:find"%s$" or line:find"%s%S-$" or lineLength
	  line = line:sub(1,endSpace)
	  text = text:sub(#line+1)
	else
	  text = ""
	end
	if center then
	  line = string.rep(" ",math.max(math.floor((lineLength-#line)/2),0))..line
	  line = line..string.rep(" ",math.max(lineLength-#line,0))
	end
	tLines[#tLines+1] = line
  end
  return tLines
end
Though to be honest, if you're only interested in specific lengths, you should just stick to what you currently have.
augustas656 #7
Posted 28 May 2014 - 05:58 PM
Also instead of doing something like table[#table + 1], I think table.insert is much much better. It's designed for the task.
Dog #8
Posted 28 May 2014 - 06:04 PM
Why bother with table.insert if you're putting something at the end of the list? I can't see the benefit of invoking an entire api to do what table[#table + 1] does. Apologies if I'm missing something obvious.
augustas656 #9
Posted 28 May 2014 - 06:08 PM
Well, invoking, you're just calling a function from a loaded table. And table.insert is specifically design for inserting a value into a table, perhaps it is more efficient than just table[#table + 1] in certain situations. Well just guessing, anyway… Also #table will give you the first nil value, so it's also efficient. Just incase, if there are certain situations where table.insert is better for insertion, then I think I'll just use table.insert
Dog #10
Posted 28 May 2014 - 06:09 PM
Gotcha - thanks, augustas656 :)/>
Lyqyd #11
Posted 28 May 2014 - 06:15 PM
He's wrong.

If speed matters t[#t + 1] = v is the way to go. It is definitely more efficient. There are no cases I can think of where the table.insert would be faster, barring LuaJ craziness. Actual test results would be rather important if one really wants to try to make the claim that table.insert is faster.
augustas656 #12
Posted 28 May 2014 - 06:37 PM
Not faster, I'm saying perhaps there are certain situations where table.insert should be used to get a table that may is better arranged, perhaps there are some types of tables or something that need table.insert, I'm not sure just guessing, because table.insert is designed for insertion perhaps there inserting without giving the place has more than just doing table[#table + 1] to it. Speed is my concern only if I am 100% sure that the way I'm doing can be faster but with the same outcomes in all situations.

EDIT: A short version of what he asked was and what I answered why don't you do it the way that uses up less computer power, and I replied perhaps there are some circumstances where table.insert is the way to go to get a well-arranged table.

Also, can I ask, you see how the function write writes lines in words, so it goes to the next line and makes sures that the words are there. So basically if there are only 10 characters width it would write test12 testabc as test12 and then testabc rather than test12 tes and then tabc. How could I do this with splitting text, so splitting text into lines of words rather than just each character.
Edited on 28 May 2014 - 04:40 PM
Bubba #13
Posted 28 May 2014 - 06:40 PM
Not faster, I'm saying perhaps there are certain situations where table.insert should be used to get a table that may is better arranged, perhaps there are some types of tables or something that need table.insert, I'm not sure just guessing, because table.insert is designed for insertion perhaps there inserting without giving the place has more than just doing table[#table + 1] to it. Speed is my concern only if I am 100% sure that the way I'm doing can be faster but with the same outcomes in all situations.

According to this post on stackoverflow, table.insert is simply there for backwards-compatibility with Lua-5.0. As far as I'm aware there is nothing special about table.insert.
Edited on 28 May 2014 - 04:41 PM
augustas656 #14
Posted 28 May 2014 - 06:41 PM
Maybe there's not, you see I didn't know that, and that explains why I said, I'm not sure, speed is only my concern if I'm 100% sure, I don't know the code for table.insert. Therefore I took a guess, and JUST incase I used it, just INCASE IF there actually IS anything special about it.
Bubba #15
Posted 28 May 2014 - 06:44 PM
Maybe there's not, you see I didn't know that, and that explains why I said, I'm not sure, speed is only my concern if I'm 100% sure, I don't know the code for table.insert. Therefore I took a guess, and JUST incase I used it, just INCASE IF there actually IS anything special about it.

No need to get defensive - I'm not trying to get on your case for making a reasonable guess. I can't speak for Lyqyd, but personally I just wanted to set facts straight.
augustas656 #16
Posted 28 May 2014 - 06:48 PM
I appreciate you telling me about table.insert, and I will change it. But, I'm just trying to explain myself, and I capitalise words to indicate the exact thing I'm trying to explain, certain grammar and word layout can mean different things even if it looks identical. But, again, thanks :)/>

Should I make a new topic asking about splitting lines by the given area based on words? Because it uses a different method, and I'm eager to find out what could be the most efficient way.

Regards
Augustas
Bubba #17
Posted 28 May 2014 - 06:50 PM
I appreciate you telling me about table.insert, and I will change it. But, I'm just trying to explain myself, and I capitalise words to indicate the exact thing I'm trying to explain, certain grammar and word layout can mean different things even if it looks identical. But, again, thanks :)/>/>

Should I make a new topic asking about splitting lines by the given area based on words? Because it uses a different method, and I'm eager to find out what could be the most efficient way.

Regards
Augustas

It would be preferable if you kept it in this topic and just changed the title to reflect the new question (perhaps you could put [unanswered] in to indicate that you have more questions).
CometWolf #18
Posted 28 May 2014 - 06:52 PM
Considering what table.insert actually does, your reasoning isn't very sensible. Anyways, again what you're looking for would be the function i posted :P/>
augustas656 #19
Posted 28 May 2014 - 07:02 PM
Yeah I am using something like that, and again maybe I'm getting you wrong or you haven't read, my reasoning can be judged overall as unsensible, but compatability to all possible situations is a higher priority to me. As I've said, I don't know table.insert's code, I just know what it can do, and I thought just incase if there is anything more to it, I'll use table.insert. Anyway

Regards
Augustas
Edited on 28 May 2014 - 05:03 PM
Lyqyd #20
Posted 28 May 2014 - 07:05 PM
Table.insert is useful for inserting values mid-table, as it will move around other values in the table to accommodate the new entry. For putting entries at the end, either can be used, though there is nothing that makes table.insert better in the simple value appending case. I thought it necessary to correct the misconception because you were talking about it in seemingly authoritative tones. It is important to promote good information.

Anyway, you could read through the write function ComputerCraft uses (it is implemented in the bios) to see how it works.
augustas656 #21
Posted 28 May 2014 - 07:11 PM
Yes in this very thread I've been already told that table.insert has nothing more special if adding to the end, I'm just constantly explaining why I had earlier thought to use table.insert rather than the simple table[#table + 1]. And, I wasn't critically thinking about checking out the table.insert's code because in this question I just wanted to find out what others think is the most efficient way, who's answers I may partly use. Thank you though anyway.

Regards
Augustas
augustas656 #22
Posted 28 May 2014 - 10:47 PM
New Question
How could I create a function that splits the given string into lines by the given width based on words, a good example would be a word document, if you write a series of words, the last word on the line if too long will not be split in-half and the other half being on a new line, the word goes to the new line, but if you have a word that is longer than the entire width then it will actually split, how could I create this?

Example
Width is 10 in these examples
————–
This is the way of tommorow
=
This is
the way
of
tommorow
—————
Lua programming is okay
=
Lua
programmin
g is okay
—————
How could I do this?

Regards
Augustas
apemanzilla #23
Posted 28 May 2014 - 10:55 PM
New Question
How could I create a function that splits the given string into lines by the given width based on words, a good example would be a word document, if you write a series of words, the last word on the line if too long will not be split in-half and the other half being on a new line, the word goes to the new line, but if you have a word that is longer than the entire width then it will actually split, how could I create this?

Example
Width is 10 in these examples
————–
This is the way of tommorow
=
This is
the way
of
tommorow
—————
Lua programming is okay
=
Lua
programmin
g is okay
—————
How could I do this?

Regards
Augustas
Both my function and CometWolf's do this. Go back a page.
augustas656 #24
Posted 28 May 2014 - 11:02 PM
New Question
How could I create a function that splits the given string into lines by the given width based on words, a good example would be a word document, if you write a series of words, the last word on the line if too long will not be split in-half and the other half being on a new line, the word goes to the new line, but if you have a word that is longer than the entire width then it will actually split, how could I create this?

Example
Width is 10 in these examples
————–
This is the way of tommorow
=
This is
the way
of
tommorow
—————
Lua programming is okay
=
Lua
programmin
g is okay
—————
How could I do this?

Regards
Augustas
Both my function and CometWolf's do this. Go back a page.

CometWolf's doesn't split the same way, it breaks off words, it just splits by line and doesn't regard spaces and words. I'm going to look at yours in more detail.
apemanzilla #25
Posted 28 May 2014 - 11:03 PM
His second one takes words and spaces into account.
augustas656 #26
Posted 28 May 2014 - 11:05 PM
Nvm .
Edited on 28 May 2014 - 09:06 PM
augustas656 #27
Posted 29 May 2014 - 02:26 PM
Okay, I wanted to try and make my own code for the new question and I think I managed, with the help of your codes, can you help me debug it? I don't want to run the API yet, when I finish the API I will constantly debug it until there's no errors, but right now, any errors that you can spot by eye would be appreciated if you informed me:


  function k.splitWordLines(text, area, newline)
  k.validType("string", text)
  local area = area or k.findW()
  local words = k.splitWords(text)
  local split = {}
    for i, word in ipairs(words) do
      if #word > area then
      table.insert(words, i + 1, word:sub(area + 1))
      words[i] = word:sub(1, area)
      end
    end
    for i, word in ipairs(words) do
      if split[1] == nil then split[1] = word
      elseif #(split[#split]..word) > area then
      split[#split + 1] = word
      else
      split[#split] = split[#split]..word
      end
      if #split[#split] < area then
      split[#split] = split[#split].." "
      end
    end
    if newline == true then
      for i, line in pairs(split) do
        if line:find("\n") then
        local new = k.splitNewline(line)
          for ins = 1, #new do
          table.insert(split, ins - 1, new[i])
          end
        end
      end
    end
  end

Also, I will only provide part of my API, but my API is like window API so you can call your window whatever and then run the functions as you can see there are k. behind the function and I also call k.validType which checks if the parameter is the said type otherwise it fires an error. k.findW() finds screen width. k.splitWords splits the text where ever there's no spaces.

Regards
Augustas