Remove duplicates from array of non-equatable objects
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I have an array of non-Equatable
objects. I want to remove all the duplicates from the array.
class SearchResult // non-equatable
var index: Int!
var score: Int!
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
var searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
searchResults.map $0.index
gives us: 0, 1, 1, 2, 2
Currently, I do so by mapping the objects' property index
, which is equatable, since it's an Int
. This way, I can remove the duplicates from the indices array:
let withDupes: [SearchResult] = searchResults
let indices = withDupes.map $0.index .removingDuplicates()
For reference, .removingDuplicates()
comes from a post on StackOverflow
Note: this is the part I want to be reviewed.
Then, I get the object with the corresponding index by looping through the indices array and add it to the noDupes
array.
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = (withDupes.filter $0.index == index ).first else return
noDupes.append(notADupe)
noDupes.map $0.index
now is: 0, 1, 2
Now, this code works, but it will be executed very often. Since I feel like this is not a very efficient way to remove the duplicates, I fear a performance drop.
How can I improve and / or simplify my code and still successfully remove the duplicates from the array?
array swift
add a comment |Â
up vote
1
down vote
favorite
I have an array of non-Equatable
objects. I want to remove all the duplicates from the array.
class SearchResult // non-equatable
var index: Int!
var score: Int!
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
var searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
searchResults.map $0.index
gives us: 0, 1, 1, 2, 2
Currently, I do so by mapping the objects' property index
, which is equatable, since it's an Int
. This way, I can remove the duplicates from the indices array:
let withDupes: [SearchResult] = searchResults
let indices = withDupes.map $0.index .removingDuplicates()
For reference, .removingDuplicates()
comes from a post on StackOverflow
Note: this is the part I want to be reviewed.
Then, I get the object with the corresponding index by looping through the indices array and add it to the noDupes
array.
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = (withDupes.filter $0.index == index ).first else return
noDupes.append(notADupe)
noDupes.map $0.index
now is: 0, 1, 2
Now, this code works, but it will be executed very often. Since I feel like this is not a very efficient way to remove the duplicates, I fear a performance drop.
How can I improve and / or simplify my code and still successfully remove the duplicates from the array?
array swift
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have an array of non-Equatable
objects. I want to remove all the duplicates from the array.
class SearchResult // non-equatable
var index: Int!
var score: Int!
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
var searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
searchResults.map $0.index
gives us: 0, 1, 1, 2, 2
Currently, I do so by mapping the objects' property index
, which is equatable, since it's an Int
. This way, I can remove the duplicates from the indices array:
let withDupes: [SearchResult] = searchResults
let indices = withDupes.map $0.index .removingDuplicates()
For reference, .removingDuplicates()
comes from a post on StackOverflow
Note: this is the part I want to be reviewed.
Then, I get the object with the corresponding index by looping through the indices array and add it to the noDupes
array.
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = (withDupes.filter $0.index == index ).first else return
noDupes.append(notADupe)
noDupes.map $0.index
now is: 0, 1, 2
Now, this code works, but it will be executed very often. Since I feel like this is not a very efficient way to remove the duplicates, I fear a performance drop.
How can I improve and / or simplify my code and still successfully remove the duplicates from the array?
array swift
I have an array of non-Equatable
objects. I want to remove all the duplicates from the array.
class SearchResult // non-equatable
var index: Int!
var score: Int!
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
var searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
searchResults.map $0.index
gives us: 0, 1, 1, 2, 2
Currently, I do so by mapping the objects' property index
, which is equatable, since it's an Int
. This way, I can remove the duplicates from the indices array:
let withDupes: [SearchResult] = searchResults
let indices = withDupes.map $0.index .removingDuplicates()
For reference, .removingDuplicates()
comes from a post on StackOverflow
Note: this is the part I want to be reviewed.
Then, I get the object with the corresponding index by looping through the indices array and add it to the noDupes
array.
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = (withDupes.filter $0.index == index ).first else return
noDupes.append(notADupe)
noDupes.map $0.index
now is: 0, 1, 2
Now, this code works, but it will be executed very often. Since I feel like this is not a very efficient way to remove the duplicates, I fear a performance drop.
How can I improve and / or simplify my code and still successfully remove the duplicates from the array?
array swift
edited Mar 19 at 16:27
asked Mar 19 at 15:43
LinusGeffarth
1115
1115
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
First note that there is no need to make the SearchResult
properties (implicitly unwrapped) optionals, as they are always initialized:
class SearchResult // non-equatable
var index: Int
var score: Int
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
Your approach is indeed not optimal, after determining a unique set of property
values, the original array must be searched for each value separately. This lookup can be improved slightly by using first(where:)
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = withDupes.first(where: $0.index == index ) else return
noDupes.append(notADupe)
The advantage of first(where:)
over filter + first
is that it short-circuits and does not create an intermediate array.
But a better and faster solution would be to modify the removingDuplicates()
method so that it compares the array elements according to a given key (which is
then required to be Equatable
).
Here is a possible implementation, using the âÂÂSmart KeyPathâ feature
from Swift 4:
extension Array
func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = [T]()
for value in self
let key = value[keyPath: key]
if !seen.contains(key)
seen.append(key)
result.append(value)
return result
This can then simply be used as
let searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
let withoutDuplicates = searchResults.removingDuplicates(byKey: .index)
It might also be worth to add another specialization for the case that the key isHashable
(as it is in your example) because then the seen
array can be replaced by a set,
which improves the lookup speed from O(N)
to O(1)
:
extension Array
func removingDuplicates<T: Hashable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(value[keyPath: key]).inserted
result.append(value)
return result
Note that if seen.insert(key).inserted
does the âÂÂtest and insert if not presentâ with a single call.
Another (more flexible) option is to pass a closure to the function which determines
the uniquing key for each element:
extension Array
func removingDuplicates<T: Hashable>(byKey key: (Element) -> T) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(key(value)).inserted
result.append(value)
return result
Example:
let withoutDuplicates = searchResults.removingDuplicates(byKey: $0.index )
Hey, this looks awesome. Thanks so far! I'm getting an error on.removingDuplicates(byKey: .index)
sayingType of expression is ambiguous without more context
. Is the backslash intended or is that a typo?
â LinusGeffarth
Mar 19 at 16:41
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â TheremovingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.
â Martin R
Mar 19 at 21:50
1
Surely a filter with side effects is a lesser sin than reinventing thefilter
wheel
â Alexander
Mar 21 at 19:20
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
 |Â
show 7 more comments
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
First note that there is no need to make the SearchResult
properties (implicitly unwrapped) optionals, as they are always initialized:
class SearchResult // non-equatable
var index: Int
var score: Int
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
Your approach is indeed not optimal, after determining a unique set of property
values, the original array must be searched for each value separately. This lookup can be improved slightly by using first(where:)
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = withDupes.first(where: $0.index == index ) else return
noDupes.append(notADupe)
The advantage of first(where:)
over filter + first
is that it short-circuits and does not create an intermediate array.
But a better and faster solution would be to modify the removingDuplicates()
method so that it compares the array elements according to a given key (which is
then required to be Equatable
).
Here is a possible implementation, using the âÂÂSmart KeyPathâ feature
from Swift 4:
extension Array
func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = [T]()
for value in self
let key = value[keyPath: key]
if !seen.contains(key)
seen.append(key)
result.append(value)
return result
This can then simply be used as
let searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
let withoutDuplicates = searchResults.removingDuplicates(byKey: .index)
It might also be worth to add another specialization for the case that the key isHashable
(as it is in your example) because then the seen
array can be replaced by a set,
which improves the lookup speed from O(N)
to O(1)
:
extension Array
func removingDuplicates<T: Hashable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(value[keyPath: key]).inserted
result.append(value)
return result
Note that if seen.insert(key).inserted
does the âÂÂtest and insert if not presentâ with a single call.
Another (more flexible) option is to pass a closure to the function which determines
the uniquing key for each element:
extension Array
func removingDuplicates<T: Hashable>(byKey key: (Element) -> T) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(key(value)).inserted
result.append(value)
return result
Example:
let withoutDuplicates = searchResults.removingDuplicates(byKey: $0.index )
Hey, this looks awesome. Thanks so far! I'm getting an error on.removingDuplicates(byKey: .index)
sayingType of expression is ambiguous without more context
. Is the backslash intended or is that a typo?
â LinusGeffarth
Mar 19 at 16:41
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â TheremovingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.
â Martin R
Mar 19 at 21:50
1
Surely a filter with side effects is a lesser sin than reinventing thefilter
wheel
â Alexander
Mar 21 at 19:20
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
 |Â
show 7 more comments
up vote
1
down vote
accepted
First note that there is no need to make the SearchResult
properties (implicitly unwrapped) optionals, as they are always initialized:
class SearchResult // non-equatable
var index: Int
var score: Int
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
Your approach is indeed not optimal, after determining a unique set of property
values, the original array must be searched for each value separately. This lookup can be improved slightly by using first(where:)
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = withDupes.first(where: $0.index == index ) else return
noDupes.append(notADupe)
The advantage of first(where:)
over filter + first
is that it short-circuits and does not create an intermediate array.
But a better and faster solution would be to modify the removingDuplicates()
method so that it compares the array elements according to a given key (which is
then required to be Equatable
).
Here is a possible implementation, using the âÂÂSmart KeyPathâ feature
from Swift 4:
extension Array
func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = [T]()
for value in self
let key = value[keyPath: key]
if !seen.contains(key)
seen.append(key)
result.append(value)
return result
This can then simply be used as
let searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
let withoutDuplicates = searchResults.removingDuplicates(byKey: .index)
It might also be worth to add another specialization for the case that the key isHashable
(as it is in your example) because then the seen
array can be replaced by a set,
which improves the lookup speed from O(N)
to O(1)
:
extension Array
func removingDuplicates<T: Hashable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(value[keyPath: key]).inserted
result.append(value)
return result
Note that if seen.insert(key).inserted
does the âÂÂtest and insert if not presentâ with a single call.
Another (more flexible) option is to pass a closure to the function which determines
the uniquing key for each element:
extension Array
func removingDuplicates<T: Hashable>(byKey key: (Element) -> T) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(key(value)).inserted
result.append(value)
return result
Example:
let withoutDuplicates = searchResults.removingDuplicates(byKey: $0.index )
Hey, this looks awesome. Thanks so far! I'm getting an error on.removingDuplicates(byKey: .index)
sayingType of expression is ambiguous without more context
. Is the backslash intended or is that a typo?
â LinusGeffarth
Mar 19 at 16:41
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â TheremovingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.
â Martin R
Mar 19 at 21:50
1
Surely a filter with side effects is a lesser sin than reinventing thefilter
wheel
â Alexander
Mar 21 at 19:20
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
 |Â
show 7 more comments
up vote
1
down vote
accepted
up vote
1
down vote
accepted
First note that there is no need to make the SearchResult
properties (implicitly unwrapped) optionals, as they are always initialized:
class SearchResult // non-equatable
var index: Int
var score: Int
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
Your approach is indeed not optimal, after determining a unique set of property
values, the original array must be searched for each value separately. This lookup can be improved slightly by using first(where:)
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = withDupes.first(where: $0.index == index ) else return
noDupes.append(notADupe)
The advantage of first(where:)
over filter + first
is that it short-circuits and does not create an intermediate array.
But a better and faster solution would be to modify the removingDuplicates()
method so that it compares the array elements according to a given key (which is
then required to be Equatable
).
Here is a possible implementation, using the âÂÂSmart KeyPathâ feature
from Swift 4:
extension Array
func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = [T]()
for value in self
let key = value[keyPath: key]
if !seen.contains(key)
seen.append(key)
result.append(value)
return result
This can then simply be used as
let searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
let withoutDuplicates = searchResults.removingDuplicates(byKey: .index)
It might also be worth to add another specialization for the case that the key isHashable
(as it is in your example) because then the seen
array can be replaced by a set,
which improves the lookup speed from O(N)
to O(1)
:
extension Array
func removingDuplicates<T: Hashable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(value[keyPath: key]).inserted
result.append(value)
return result
Note that if seen.insert(key).inserted
does the âÂÂtest and insert if not presentâ with a single call.
Another (more flexible) option is to pass a closure to the function which determines
the uniquing key for each element:
extension Array
func removingDuplicates<T: Hashable>(byKey key: (Element) -> T) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(key(value)).inserted
result.append(value)
return result
Example:
let withoutDuplicates = searchResults.removingDuplicates(byKey: $0.index )
First note that there is no need to make the SearchResult
properties (implicitly unwrapped) optionals, as they are always initialized:
class SearchResult // non-equatable
var index: Int
var score: Int
init(_ index: Int, score: Int = 0)
self.index = index
self.score = score
Your approach is indeed not optimal, after determining a unique set of property
values, the original array must be searched for each value separately. This lookup can be improved slightly by using first(where:)
var noDupes: [SearchResult] =
indices.forEach (index) in
guard let notADupe = withDupes.first(where: $0.index == index ) else return
noDupes.append(notADupe)
The advantage of first(where:)
over filter + first
is that it short-circuits and does not create an intermediate array.
But a better and faster solution would be to modify the removingDuplicates()
method so that it compares the array elements according to a given key (which is
then required to be Equatable
).
Here is a possible implementation, using the âÂÂSmart KeyPathâ feature
from Swift 4:
extension Array
func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = [T]()
for value in self
let key = value[keyPath: key]
if !seen.contains(key)
seen.append(key)
result.append(value)
return result
This can then simply be used as
let searchResults: [SearchResult] = [SearchResult(0), SearchResult(1), SearchResult(1), SearchResult(2), SearchResult(2)]
let withoutDuplicates = searchResults.removingDuplicates(byKey: .index)
It might also be worth to add another specialization for the case that the key isHashable
(as it is in your example) because then the seen
array can be replaced by a set,
which improves the lookup speed from O(N)
to O(1)
:
extension Array
func removingDuplicates<T: Hashable>(byKey key: KeyPath<Element, T>) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(value[keyPath: key]).inserted
result.append(value)
return result
Note that if seen.insert(key).inserted
does the âÂÂtest and insert if not presentâ with a single call.
Another (more flexible) option is to pass a closure to the function which determines
the uniquing key for each element:
extension Array
func removingDuplicates<T: Hashable>(byKey key: (Element) -> T) -> [Element]
var result = [Element]()
var seen = Set<T>()
for value in self
if seen.insert(key(value)).inserted
result.append(value)
return result
Example:
let withoutDuplicates = searchResults.removingDuplicates(byKey: $0.index )
edited Mar 21 at 19:26
answered Mar 19 at 16:34
Martin R
14k12257
14k12257
Hey, this looks awesome. Thanks so far! I'm getting an error on.removingDuplicates(byKey: .index)
sayingType of expression is ambiguous without more context
. Is the backslash intended or is that a typo?
â LinusGeffarth
Mar 19 at 16:41
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â TheremovingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.
â Martin R
Mar 19 at 21:50
1
Surely a filter with side effects is a lesser sin than reinventing thefilter
wheel
â Alexander
Mar 21 at 19:20
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
 |Â
show 7 more comments
Hey, this looks awesome. Thanks so far! I'm getting an error on.removingDuplicates(byKey: .index)
sayingType of expression is ambiguous without more context
. Is the backslash intended or is that a typo?
â LinusGeffarth
Mar 19 at 16:41
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â TheremovingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.
â Martin R
Mar 19 at 21:50
1
Surely a filter with side effects is a lesser sin than reinventing thefilter
wheel
â Alexander
Mar 21 at 19:20
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
Hey, this looks awesome. Thanks so far! I'm getting an error on
.removingDuplicates(byKey: .index)
saying Type of expression is ambiguous without more context
. Is the backslash intended or is that a typo?â LinusGeffarth
Mar 19 at 16:41
Hey, this looks awesome. Thanks so far! I'm getting an error on
.removingDuplicates(byKey: .index)
saying Type of expression is ambiguous without more context
. Is the backslash intended or is that a typo?â LinusGeffarth
Mar 19 at 16:41
1
1
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â The
removingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.â Martin R
Mar 19 at 21:50
@LinusGeffarth: I don't know if the key paths work for implicitly unwrapped optionals. I have added another option (passing a closure) which is more flexible and that would work with an IUO as well. â The
removingDuplicates<T: Equatable>
should be faster because it traverses the array only once, whereas your solution traverses the array again for each key.â Martin R
Mar 19 at 21:50
1
1
Surely a filter with side effects is a lesser sin than reinventing the
filter
wheelâ Alexander
Mar 21 at 19:20
Surely a filter with side effects is a lesser sin than reinventing the
filter
wheelâ Alexander
Mar 21 at 19:20
1
1
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
@Alexander: Actually I should have known about that approach: stackoverflow.com/a/42542237/1187415 :)
â Martin R
Mar 22 at 14:40
1
1
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
@MartinR We've all been there hah stackoverflow.com/a/40579948/3141234
â Alexander
Mar 22 at 15:53
 |Â
show 7 more comments
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
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f189941%2fremove-duplicates-from-array-of-non-equatable-objects%23new-answer', 'question_page');
);
Post as a guest
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
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
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