Kosmetika
Kosmetika

Reputation: 21314

Find the last index of a character in a string

I want to have ability to use a lastIndexOf method for the strings in my Lua (Luvit) project. Unfortunately there's no such method built-in and I'm bit stuck now.

In Javascript it looks like:

'my.string.here.'.lastIndexOf('.')     // returns 14

Upvotes: 10

Views: 7887

Answers (5)

oria general
oria general

Reputation: 273

Can be optimized but simple and does the work.

function lastIndexOf(haystack, needle)
  local last_index = 0
  while haystack:sub(last_index+1, haystack:len()):find(needle) ~= nil do
    last_index = last_index + haystack:sub(last_index+1, haystack:len()):find(needle)
  end
  return last_index
end

local s = 'my.string.here.'
print(lastIndexOf(s, '%.')) -- 15

Upvotes: 0

joews
joews

Reputation: 30340

To search for the last instance of string needle in haystack:

function findLast(haystack, needle)
    --Set the third arg to false to allow pattern matching
    local found = haystack:reverse():find(needle:reverse(), nil, true)
    if found then
        return haystack:len() - needle:len() - found + 2 
    else
        return found
    end
end

print(findLast("my.string.here.", ".")) -- 15, because Lua strings are 1-indexed
print(findLast("my.string.here.", "here")) -- 11
print(findLast("my.string.here.", "there")) -- nil

If you want to search for the last instance of a pattern instead, change the last argument to find to false (or remove it).

Upvotes: 4

lhf
lhf

Reputation: 72412

function findLast(haystack, needle)
    local i=haystack:match(".*"..needle.."()")
    if i==nil then return nil else return i-1 end
end
s='my.string.here.'
print(findLast(s,"%."))
print(findLast(s,"e"))

Note that to find . you need to escape it.

Upvotes: 10

Philipp Gesang
Philipp Gesang

Reputation: 526

Here’s a solution using LPeg’s position capture.

local lpeg      = require "lpeg"
local Cp, P     = lpeg.Cp, lpeg.P
local lpegmatch = lpeg.match

local cache = { }

local find_last = function (str, substr)
  if not (str and substr)
    or str == "" or substr == ""
  then
    return nil
  end
  local pat = cache [substr]
  if not pat then
    local p_substr   = P (substr)
    local last       = Cp() * p_substr * Cp() * (1 - p_substr)^0 * -1
    pat = (1 - last)^0 * last
    cache [substr] = pat
  end
  return lpegmatch (pat, str)
end

find_last() finds the last occurence of substr in the string str, where substr can be a string of any length. The first return value is the position of the first character of substr in str, the second return value is the position of the first character following substr (i.e. it equals the length of the match plus the first return value).

Usage:

local tests = {
  A    = [[fooA]],                      --> 4, 5
  [""] = [[foo]],                       --> nil
  FOO  = [[]],                          --> nil
  K    = [[foo]],                       --> nil
  X    = [[X foo X bar X baz]],         --> 13, 14
  XX   = [[foo XX X XY bar XX baz X]],  --> 17, 19
  Y    = [[YYYYYYYYYYYYYYYYYY]],        --> 18, 19
  ZZZ  = [[ZZZZZZZZZZZZZZZZZZ]],        --> 14, 17
  --- Accepts patterns as well!
  [P"X" * lpeg.R"09"^1] = [[fooX42barXxbazX]], --> 4, 7
}

for substr, str in next, tests do
  print (">>", substr, str, "->", find_last (str, substr))
end

Upvotes: 3

Ryan Stein
Ryan Stein

Reputation: 8000

If you have performance concerns, then this might be a bit faster if you're using Luvit which uses LuaJIT.

local find = string.find
local function lastIndexOf(haystack, needle)
    local i, j
    local k = 0
    repeat
        i = j
        j, k = find(haystack, needle, k + 1, true)
    until j == nil

    return i
end

local s = 'my.string.here.'
print(lastIndexOf(s, '.')) -- This will be 15.

Keep in mind that Lua strings begin at 1 instead of 0 as in JavaScript.

Upvotes: 4

Related Questions