Find person and immediate neighbours in Seq[Person]












2














Given a Seq[Person], which contains 1-n Persons (and the minimum 1 Person beeing "Tom"), what is the easiest approach to find a Person with name "Tom" as well as the Person right before Tome and the Person right after Tom?



More detailed explanation:



case class Person(name:String)


The list of persons can be arbitrarily long, but will have at least one entry, which must be "Tom". So those lists can be a valid case:



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"))


You get the idea. Since I already have "Tom", the task is to get his left neighbour (if it exists), and the right neighbour (if it exists).



What is the most efficient way to achieve to do this in scala?





My current approach:



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`
}
}


Just doesn't feel like I am doing it the scala way.





Solution by Mukesh prajapati:



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

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


Pretty short, seems to cover all cases.





Solution by anuj saxena



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
}









share|improve this question
























  • Is Person a case class?
    – anuj saxena
    Nov 22 at 16:15












  • Yes. Updated the question to clarify.
    – user826955
    Nov 23 at 8:03










  • The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
    – jwvh
    Nov 23 at 8:27










  • In this case, index == -1 should be checked to avoid the issue, so its not a problem.
    – user826955
    Nov 23 at 8:28
















2














Given a Seq[Person], which contains 1-n Persons (and the minimum 1 Person beeing "Tom"), what is the easiest approach to find a Person with name "Tom" as well as the Person right before Tome and the Person right after Tom?



More detailed explanation:



case class Person(name:String)


The list of persons can be arbitrarily long, but will have at least one entry, which must be "Tom". So those lists can be a valid case:



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"))


You get the idea. Since I already have "Tom", the task is to get his left neighbour (if it exists), and the right neighbour (if it exists).



What is the most efficient way to achieve to do this in scala?





My current approach:



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`
}
}


Just doesn't feel like I am doing it the scala way.





Solution by Mukesh prajapati:



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

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


Pretty short, seems to cover all cases.





Solution by anuj saxena



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
}









share|improve this question
























  • Is Person a case class?
    – anuj saxena
    Nov 22 at 16:15












  • Yes. Updated the question to clarify.
    – user826955
    Nov 23 at 8:03










  • The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
    – jwvh
    Nov 23 at 8:27










  • In this case, index == -1 should be checked to avoid the issue, so its not a problem.
    – user826955
    Nov 23 at 8:28














2












2








2







Given a Seq[Person], which contains 1-n Persons (and the minimum 1 Person beeing "Tom"), what is the easiest approach to find a Person with name "Tom" as well as the Person right before Tome and the Person right after Tom?



More detailed explanation:



case class Person(name:String)


The list of persons can be arbitrarily long, but will have at least one entry, which must be "Tom". So those lists can be a valid case:



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"))


You get the idea. Since I already have "Tom", the task is to get his left neighbour (if it exists), and the right neighbour (if it exists).



What is the most efficient way to achieve to do this in scala?





My current approach:



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`
}
}


Just doesn't feel like I am doing it the scala way.





Solution by Mukesh prajapati:



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

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


Pretty short, seems to cover all cases.





Solution by anuj saxena



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
}









share|improve this question















Given a Seq[Person], which contains 1-n Persons (and the minimum 1 Person beeing "Tom"), what is the easiest approach to find a Person with name "Tom" as well as the Person right before Tome and the Person right after Tom?



More detailed explanation:



case class Person(name:String)


The list of persons can be arbitrarily long, but will have at least one entry, which must be "Tom". So those lists can be a valid case:



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"))


You get the idea. Since I already have "Tom", the task is to get his left neighbour (if it exists), and the right neighbour (if it exists).



What is the most efficient way to achieve to do this in scala?





My current approach:



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`
}
}


Just doesn't feel like I am doing it the scala way.





Solution by Mukesh prajapati:



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

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


Pretty short, seems to cover all cases.





Solution by anuj saxena



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
}






scala collections filtering sliding-window






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 at 8:57

























asked Nov 22 at 14:24









user826955

1,02011638




1,02011638












  • Is Person a case class?
    – anuj saxena
    Nov 22 at 16:15












  • Yes. Updated the question to clarify.
    – user826955
    Nov 23 at 8:03










  • The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
    – jwvh
    Nov 23 at 8:27










  • In this case, index == -1 should be checked to avoid the issue, so its not a problem.
    – user826955
    Nov 23 at 8:28


















  • Is Person a case class?
    – anuj saxena
    Nov 22 at 16:15












  • Yes. Updated the question to clarify.
    – user826955
    Nov 23 at 8:03










  • The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
    – jwvh
    Nov 23 at 8:27










  • In this case, index == -1 should be checked to avoid the issue, so its not a problem.
    – user826955
    Nov 23 at 8:28
















Is Person a case class?
– anuj saxena
Nov 22 at 16:15






Is Person a case class?
– anuj saxena
Nov 22 at 16:15














Yes. Updated the question to clarify.
– user826955
Nov 23 at 8:03




Yes. Updated the question to clarify.
– user826955
Nov 23 at 8:03












The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
– jwvh
Nov 23 at 8:27




The solution from Mukesh is good, but it will give wrong/misleading results if "Tom" is actually not in the collection.
– jwvh
Nov 23 at 8:27












In this case, index == -1 should be checked to avoid the issue, so its not a problem.
– user826955
Nov 23 at 8:28




In this case, index == -1 should be checked to avoid the issue, so its not a problem.
– user826955
Nov 23 at 8:28












5 Answers
5






active

oldest

votes


















2














First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:



index = persons.indexOf("Tom")
doSomethingWith(persons.lift(index-1), persons.lift(index+1))





share|improve this answer























  • This seems to be the shortest and best answer to far.
    – user826955
    Nov 23 at 8:01






  • 1




    You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
    – Felix
    Nov 23 at 20:05






  • 1




    yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
    – Mukesh prajapati
    Nov 23 at 21:52



















1














A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).



If we want to use indexes, we better use Array as it provides us random access.



So to the current solution, here is my code to find prev and next element of a certain data in a Seq or 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
}


Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).



This code will pick the pair on the first occurrence of the person provided in the parameter.



If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.



Update



If Person is a case class then we can use deep match like this:



  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
}


For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):



  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
}


}






share|improve this answer























  • This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
    – user826955
    Nov 23 at 7:39










  • Edited my question to clarify a bit more.
    – user826955
    Nov 23 at 8:01






  • 1




    findNeighboursV2 must solve the solution for your first comment
    – anuj saxena
    Nov 23 at 19:47










  • Confirmed, it works for all my tests.
    – user826955
    Nov 26 at 8:23



















0














You can use sliding function:



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)
}
}





share|improve this answer

















  • 1




    Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
    – user826955
    Nov 22 at 15:05












  • @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
    – Luis Miguel Mejía Suárez
    Nov 22 at 15:38



















0














If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:



tomIndex = persons.indexOf("Tom")
doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))





share|improve this answer

















  • 1




    You need to take care of corner cases. It will give ArrayOutOfBoundException.
    – Mukesh prajapati
    Nov 22 at 15:58



















0














// 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))


Scalafiddle






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53433033%2ffind-person-and-immediate-neighbours-in-seqperson%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:



    index = persons.indexOf("Tom")
    doSomethingWith(persons.lift(index-1), persons.lift(index+1))





    share|improve this answer























    • This seems to be the shortest and best answer to far.
      – user826955
      Nov 23 at 8:01






    • 1




      You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
      – Felix
      Nov 23 at 20:05






    • 1




      yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
      – Mukesh prajapati
      Nov 23 at 21:52
















    2














    First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:



    index = persons.indexOf("Tom")
    doSomethingWith(persons.lift(index-1), persons.lift(index+1))





    share|improve this answer























    • This seems to be the shortest and best answer to far.
      – user826955
      Nov 23 at 8:01






    • 1




      You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
      – Felix
      Nov 23 at 20:05






    • 1




      yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
      – Mukesh prajapati
      Nov 23 at 21:52














    2












    2








    2






    First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:



    index = persons.indexOf("Tom")
    doSomethingWith(persons.lift(index-1), persons.lift(index+1))





    share|improve this answer














    First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:



    index = persons.indexOf("Tom")
    doSomethingWith(persons.lift(index-1), persons.lift(index+1))






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 22 at 15:52

























    answered Nov 22 at 15:38









    Mukesh prajapati

    506317




    506317












    • This seems to be the shortest and best answer to far.
      – user826955
      Nov 23 at 8:01






    • 1




      You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
      – Felix
      Nov 23 at 20:05






    • 1




      yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
      – Mukesh prajapati
      Nov 23 at 21:52


















    • This seems to be the shortest and best answer to far.
      – user826955
      Nov 23 at 8:01






    • 1




      You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
      – Felix
      Nov 23 at 20:05






    • 1




      yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
      – Mukesh prajapati
      Nov 23 at 21:52
















    This seems to be the shortest and best answer to far.
    – user826955
    Nov 23 at 8:01




    This seems to be the shortest and best answer to far.
    – user826955
    Nov 23 at 8:01




    1




    1




    You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
    – Felix
    Nov 23 at 20:05




    You are traversing the Seq 3 times like this. (it's not an IndexedSeq)
    – Felix
    Nov 23 at 20:05




    1




    1




    yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
    – Mukesh prajapati
    Nov 23 at 21:52




    yeah... we can create Array from list(persons.toArray) and then it will take constant time to find out element at particular index.
    – Mukesh prajapati
    Nov 23 at 21:52













    1














    A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).



    If we want to use indexes, we better use Array as it provides us random access.



    So to the current solution, here is my code to find prev and next element of a certain data in a Seq or 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
    }


    Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).



    This code will pick the pair on the first occurrence of the person provided in the parameter.



    If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.



    Update



    If Person is a case class then we can use deep match like this:



      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
    }


    For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):



      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
    }


    }






    share|improve this answer























    • This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
      – user826955
      Nov 23 at 7:39










    • Edited my question to clarify a bit more.
      – user826955
      Nov 23 at 8:01






    • 1




      findNeighboursV2 must solve the solution for your first comment
      – anuj saxena
      Nov 23 at 19:47










    • Confirmed, it works for all my tests.
      – user826955
      Nov 26 at 8:23
















    1














    A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).



    If we want to use indexes, we better use Array as it provides us random access.



    So to the current solution, here is my code to find prev and next element of a certain data in a Seq or 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
    }


    Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).



    This code will pick the pair on the first occurrence of the person provided in the parameter.



    If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.



    Update



    If Person is a case class then we can use deep match like this:



      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
    }


    For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):



      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
    }


    }






    share|improve this answer























    • This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
      – user826955
      Nov 23 at 7:39










    • Edited my question to clarify a bit more.
      – user826955
      Nov 23 at 8:01






    • 1




      findNeighboursV2 must solve the solution for your first comment
      – anuj saxena
      Nov 23 at 19:47










    • Confirmed, it works for all my tests.
      – user826955
      Nov 26 at 8:23














    1












    1








    1






    A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).



    If we want to use indexes, we better use Array as it provides us random access.



    So to the current solution, here is my code to find prev and next element of a certain data in a Seq or 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
    }


    Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).



    This code will pick the pair on the first occurrence of the person provided in the parameter.



    If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.



    Update



    If Person is a case class then we can use deep match like this:



      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
    }


    For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):



      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
    }


    }






    share|improve this answer














    A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).



    If we want to use indexes, we better use Array as it provides us random access.



    So to the current solution, here is my code to find prev and next element of a certain data in a Seq or 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
    }


    Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).



    This code will pick the pair on the first occurrence of the person provided in the parameter.



    If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.



    Update



    If Person is a case class then we can use deep match like this:



      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
    }


    For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):



      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
    }


    }







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 23 at 19:46

























    answered Nov 22 at 16:00









    anuj saxena

    23917




    23917












    • This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
      – user826955
      Nov 23 at 7:39










    • Edited my question to clarify a bit more.
      – user826955
      Nov 23 at 8:01






    • 1




      findNeighboursV2 must solve the solution for your first comment
      – anuj saxena
      Nov 23 at 19:47










    • Confirmed, it works for all my tests.
      – user826955
      Nov 26 at 8:23


















    • This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
      – user826955
      Nov 23 at 7:39










    • Edited my question to clarify a bit more.
      – user826955
      Nov 23 at 8:01






    • 1




      findNeighboursV2 must solve the solution for your first comment
      – anuj saxena
      Nov 23 at 19:47










    • Confirmed, it works for all my tests.
      – user826955
      Nov 26 at 8:23
















    This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
    – user826955
    Nov 23 at 7:39




    This approach does not quite work with val persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should return None for the left neighbour, and Some("Mike") for the right neighbour. I tried to add those cases, but it seems like I have to put a lot of cases for it to cover everything.
    – user826955
    Nov 23 at 7:39












    Edited my question to clarify a bit more.
    – user826955
    Nov 23 at 8:01




    Edited my question to clarify a bit more.
    – user826955
    Nov 23 at 8:01




    1




    1




    findNeighboursV2 must solve the solution for your first comment
    – anuj saxena
    Nov 23 at 19:47




    findNeighboursV2 must solve the solution for your first comment
    – anuj saxena
    Nov 23 at 19:47












    Confirmed, it works for all my tests.
    – user826955
    Nov 26 at 8:23




    Confirmed, it works for all my tests.
    – user826955
    Nov 26 at 8:23











    0














    You can use sliding function:



    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)
    }
    }





    share|improve this answer

















    • 1




      Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
      – user826955
      Nov 22 at 15:05












    • @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
      – Luis Miguel Mejía Suárez
      Nov 22 at 15:38
















    0














    You can use sliding function:



    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)
    }
    }





    share|improve this answer

















    • 1




      Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
      – user826955
      Nov 22 at 15:05












    • @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
      – Luis Miguel Mejía Suárez
      Nov 22 at 15:38














    0












    0








    0






    You can use sliding function:



    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)
    }
    }





    share|improve this answer












    You can use sliding function:



    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)
    }
    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 22 at 14:47









    Mahmoud Hanafy

    1,05011527




    1,05011527








    • 1




      Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
      – user826955
      Nov 22 at 15:05












    • @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
      – Luis Miguel Mejía Suárez
      Nov 22 at 15:38














    • 1




      Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
      – user826955
      Nov 22 at 15:05












    • @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
      – Luis Miguel Mejía Suárez
      Nov 22 at 15:38








    1




    1




    Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
    – user826955
    Nov 22 at 15:05






    Unfortunately, this fails when the Seq is ("Tom", "Mike", "Joe") (expected result would be: left-neighbour: None, right-neighbour: Mike. The same way it fails for less than 3 persons in the list. So I think its not possible to do a sliding window variant.
    – user826955
    Nov 22 at 15:05














    @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
    – Luis Miguel Mejía Suárez
    Nov 22 at 15:38




    @MahmoudHanafy, you may try to wrap the index access with a Try(...).toOption. Or use pattern matching to check all cases.
    – Luis Miguel Mejía Suárez
    Nov 22 at 15:38











    0














    If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:



    tomIndex = persons.indexOf("Tom")
    doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))





    share|improve this answer

















    • 1




      You need to take care of corner cases. It will give ArrayOutOfBoundException.
      – Mukesh prajapati
      Nov 22 at 15:58
















    0














    If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:



    tomIndex = persons.indexOf("Tom")
    doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))





    share|improve this answer

















    • 1




      You need to take care of corner cases. It will give ArrayOutOfBoundException.
      – Mukesh prajapati
      Nov 22 at 15:58














    0












    0








    0






    If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:



    tomIndex = persons.indexOf("Tom")
    doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))





    share|improve this answer












    If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:



    tomIndex = persons.indexOf("Tom")
    doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 22 at 15:30









    Metropolis

    1,206927




    1,206927








    • 1




      You need to take care of corner cases. It will give ArrayOutOfBoundException.
      – Mukesh prajapati
      Nov 22 at 15:58














    • 1




      You need to take care of corner cases. It will give ArrayOutOfBoundException.
      – Mukesh prajapati
      Nov 22 at 15:58








    1




    1




    You need to take care of corner cases. It will give ArrayOutOfBoundException.
    – Mukesh prajapati
    Nov 22 at 15:58




    You need to take care of corner cases. It will give ArrayOutOfBoundException.
    – Mukesh prajapati
    Nov 22 at 15:58











    0














    // 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))


    Scalafiddle






    share|improve this answer




























      0














      // 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))


      Scalafiddle






      share|improve this answer


























        0












        0








        0






        // 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))


        Scalafiddle






        share|improve this answer














        // 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))


        Scalafiddle







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 23 at 21:05

























        answered Nov 23 at 20:11









        Felix

        3,90083151




        3,90083151






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53433033%2ffind-person-and-immediate-neighbours-in-seqperson%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Sphinx de Gizeh

            Dijon

            Langue