Find person and immediate neighbours in Seq[Person]
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
add a comment |
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
IsPersona 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 == -1should be checked to avoid the issue, so its not a problem.
– user826955
Nov 23 at 8:28
add a comment |
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
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
scala collections filtering sliding-window
edited Nov 26 at 8:57
asked Nov 22 at 14:24
user826955
1,02011638
1,02011638
IsPersona 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 == -1should be checked to avoid the issue, so its not a problem.
– user826955
Nov 23 at 8:28
add a comment |
IsPersona 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 == -1should 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
add a comment |
5 Answers
5
active
oldest
votes
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))
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
add a comment |
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
}
}
This approach does not quite work withval persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should returnNonefor the left neighbour, andSome("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
findNeighboursV2must 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
add a comment |
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)
}
}
1
Unfortunately, this fails when theSeqis("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 aTry(...).toOption. Or use pattern matching to check all cases.
– Luis Miguel Mejía Suárez
Nov 22 at 15:38
add a comment |
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))
1
You need to take care of corner cases. It will give ArrayOutOfBoundException.
– Mukesh prajapati
Nov 22 at 15:58
add a comment |
// 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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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))
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
add a comment |
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))
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
add a comment |
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))
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))
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
add a comment |
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
add a comment |
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
}
}
This approach does not quite work withval persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should returnNonefor the left neighbour, andSome("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
findNeighboursV2must 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
add a comment |
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
}
}
This approach does not quite work withval persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should returnNonefor the left neighbour, andSome("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
findNeighboursV2must 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
add a comment |
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
}
}
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
}
}
edited Nov 23 at 19:46
answered Nov 22 at 16:00
anuj saxena
23917
23917
This approach does not quite work withval persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should returnNonefor the left neighbour, andSome("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
findNeighboursV2must 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
add a comment |
This approach does not quite work withval persons = List(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"),Person("Joe")). In this case it should returnNonefor the left neighbour, andSome("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
findNeighboursV2must 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
add a comment |
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)
}
}
1
Unfortunately, this fails when theSeqis("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 aTry(...).toOption. Or use pattern matching to check all cases.
– Luis Miguel Mejía Suárez
Nov 22 at 15:38
add a comment |
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)
}
}
1
Unfortunately, this fails when theSeqis("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 aTry(...).toOption. Or use pattern matching to check all cases.
– Luis Miguel Mejía Suárez
Nov 22 at 15:38
add a comment |
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)
}
}
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)
}
}
answered Nov 22 at 14:47
Mahmoud Hanafy
1,05011527
1,05011527
1
Unfortunately, this fails when theSeqis("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 aTry(...).toOption. Or use pattern matching to check all cases.
– Luis Miguel Mejía Suárez
Nov 22 at 15:38
add a comment |
1
Unfortunately, this fails when theSeqis("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 aTry(...).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
add a comment |
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))
1
You need to take care of corner cases. It will give ArrayOutOfBoundException.
– Mukesh prajapati
Nov 22 at 15:58
add a comment |
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))
1
You need to take care of corner cases. It will give ArrayOutOfBoundException.
– Mukesh prajapati
Nov 22 at 15:58
add a comment |
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))
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))
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
add a comment |
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
add a comment |
// 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
add a comment |
// 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
add a comment |
// 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
// 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
edited Nov 23 at 21:05
answered Nov 23 at 20:11
Felix
3,90083151
3,90083151
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Is
Persona 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 == -1should be checked to avoid the issue, so its not a problem.– user826955
Nov 23 at 8:28