Найти человека и ближайших соседей в Seq [Person] - PullRequest
0 голосов
/ 22 ноября 2018

Учитывая Seq[Person], который содержит 1- n Person с (и минимум 1 человек, имеющий "Том"), какой самый простой способ найти Person с именем "Том ", а также Человек прямо перед Том и Человек сразу после Том?

Более подробное объяснение:

case class Person(name:String)

Списокчеловек может быть произвольно длинным, но будет иметь хотя бы одну запись , которая должна быть "Томь".Таким образом, эти списки могут быть действительными:

val caseOne =   Seq(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"))
val caseTwo =   Seq(Person("Mike"), Person("Tom"), Person("Dude"),Person("Frank"))
val caseThree = Seq(Person("Tom"))
val caseFour =  Seq(Person("Mike"), Person("Tom"))

Вы поняли идею.Поскольку у меня уже есть «Том», задача состоит в том, чтобы получить его левого соседа (если он существует) и правого соседа (если он существует).

Каков наиболее эффективный способ добиться этого вЛа Скала?


Мой текущий подход:

var result:Tuple2[Option[Person], Option[Person]] = (None,None)

for (i <- persons.indices)
{
  persons(i).name match
  {
    case "Tom" if i > 0 && i < persons.size-1 => result = (Some(persons(i-1)), Some(persons(i+1))) // (...), left, `Tom`, right, (...)
    case "Tom" if i > 0                       => result = (Some(persons(i-1)), None)               // (...), left, `Tom`
    case "Tom" if i < persons.size-1          => result = (Some(persons(i-1)), None)               // `Tom`, right, (...)
    case "Tom"                                => result = (None, None)                             // `Tom`
  }
}

Просто не чувствую, что я делаю это scala way .


Решение Мукеша Праджапати:

val arrayPersons = persons.toArray
val index = arrayPersons.indexOf(Person("Tom"))

if (index >= 0)
   result = (arrayPersons.lift(index-1), arrayPersons.lift(index+1))

Довольно короткое, кажется, охватывает все случаи.


Решение поанудж саксена

result = persons.sliding(3).foldLeft((Option.empty[Person], Option.empty[Person]))
{
    case ((Some(prev), Some(next)), _)            => (Some(prev), Some(next))
    case (_, prev :: Person(`name`) :: next :: _) => (Some(prev), Some(next))
    case (_, _ :: prev :: Person(`name`) :: _)    => (Some(prev), None)
    case (_, Person(`name`) :: next :: _)         => (None, Some(next))
    case (neighbours, _) => neighbours
}

Ответы [ 5 ]

0 голосов
/ 23 ноября 2018
// Start writing your ScalaFiddle code here
case class Person(name: String)

val persons1 = Seq(Person("Martin"),Person("John"),Person("Tom"),Person("Jack"),Person("Mary"))
val persons2 = Seq(Person("Martin"),Person("John"),Person("Tom"))
val persons3 = Seq(Person("Tom"),Person("Jack"),Person("Mary"))
val persons4 = Seq(Person("Tom"))

def f(persons:Seq[Person]) = 
  persons
    .sliding(3)
    .filter(_.contains(Person("Tom")))
    .maxBy {
      case _ :: Person("Tom") :: _ => 1
      case _                       => 0
    }
    .toList
    .take(persons.indexOf(Person("Tom")) + 2) // In the case where "Tom" is first, drop the last person
    .drop(persons.indexOf(Person("Tom")) - 1) // In the case where "Tom" is last, drop the first person

println(f(persons1)) // List(Person(John), Person(Tom), Person(Jack))
println(f(persons2)) // List(Person(John), Person(Tom))
println(f(persons3)) // List(Person(Tom), Person(Jack))
println(f(persons4)) // List(Person(Tom))

Скалафиддл

0 голосов
/ 22 ноября 2018

Практическое правило: мы никогда не должны обращаться к содержимому списка / последовательности, используя индексы, так как оно подвержено ошибкам (например, IndexNotFoundException).

Если мы хотим использовать индексы, мы лучше используемArray, поскольку он предоставляет нам произвольный доступ.

Итак, к текущему решению, вот мой код, чтобы найти prev и следующий элемент определенных данных в Seq или List:

  def findNeighbours(name: String, persons: Seq[Person]): Option[(Person, Person)] = {
    persons.sliding(3).flatMap{
      case prev :: person :: next :: Nil if person.name == name => Some(prev, next)
      case _ => None
    }.toList.headOption
  }

Здесь тип возвращаемого значения - Option, поскольку существует вероятность, что мы не сможем найти его здесь (в случае, если в списке присутствует только один человек или нужного в списке нет).

Этот код выберет пару в первом вхождении person, указанном в параметре.

Если у вас есть вероятность, что для указанного лица может быть несколько вхождений, удалите headOption впоследняя строка функции findNeighbours.Затем он вернет список кортежей.

Обновление

Если Person является классом дел, то мы можем использовать глубокое совпадение, например:

  def findNeighbours(name: String, persons: Seq[Person]): Option[(Person, Person)] = {
    persons.sliding(3).flatMap{
      case prev :: Person(`name`) :: next :: Nil => Some(prev, next)
      case _ => None
    }.toList.headOption
  }

Для вашегоВ решении необходимо добавить к нему больше случаев (изменил его, чтобы использовать один раз в случае единственного ответа):

  def findNeighboursV2(name: String, persons: Seq[Person]): (Option[Person], Option[Person]) = {
persons.sliding(3).foldLeft((Option.empty[Person], Option.empty[Person])){
    case ((Some(prev), Some(next)), _) => (Some(prev), Some(next))
    case (_, prev :: Person(`name`) :: next :: _) => (Some(prev), Some(next))
    case (_, _ :: prev :: Person(`name`) :: _) => (Some(prev), None)
    case (_, Person(`name`) :: next :: _) => (None, Some(next))
    case (neighbours, _) => neighbours
}

}

0 голосов
/ 22 ноября 2018

Если вы знаете, что в вашем Seq будет только один экземпляр "Тома", используйте indexOf вместо ручной петли:

tomIndex = persons.indexOf("Tom")
doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))
0 голосов
/ 22 ноября 2018

Сначала найдите индекс, где присутствует «Том», затем используйте «лифт».«lift» превращает частичную функцию в простую функцию, возвращающую результат Option:

index = persons.indexOf("Tom")
doSomethingWith(persons.lift(index-1), persons.lift(index+1))
0 голосов
/ 22 ноября 2018

Вы можете использовать функцию скольжения:

persons: Seq[Person] = initializePersons()
persons.sliding(size = 3).find { itr =>
  if (itr(1).name = "Tom") {
    val before = itr(0)
    val middle = itr(1)
    val after = itr(2)
  }
}
...