-
Notifications
You must be signed in to change notification settings - Fork 584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(collections): add joinToString #1223
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for implementing this! I left some minor comments.
@LionC updated. let me know if anything else you have in mind. |
collections/join_to_string.ts
Outdated
* Example: | ||
* | ||
* ```ts | ||
* import { joinToString } from "./join_to_string.ts"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #1227 , I think we should use the same import
as in the README
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, nice. that was something that bothered me a lot. I didn't know somebody fixed this in another PR.
@majidsajadi @LionC How about adding The current proposed API looks a little too trivial to me to be included in std.. |
I guess P.S. how about |
I guess |
I am fine with adding type JoinToStringOptions = {
separator?: string;
prefix?: string;
suffix?: string;
truncate?: {
after: number;
with?: string;
};
}; That will ensure that if you truncate, you need to set a limit and the optional truncate suffix thing (in kotlin you do not really have objet literals, you need to call constructors, so they tend to flatten parameters). We could also think abot adding a I want to note though @kt3k, that the current version should already be faster than |
collections/join_to_string.ts
Outdated
array: readonly T[], | ||
selector: (el: T) => string, | ||
{ | ||
separator = ", ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think space separator is more appropriate
separator = ", ", | |
separator = " ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array.join
and Kotlin's joinToString
both have the comma in their default, why should we deviate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separator = ", ", | |
separator = ",", |
Agree with @LionC here, I think it's better if it match Array.prototype.join
with a single comma without space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhangyuannie @LionC Array.prototype.join
default separator is ","
but Kotlin's joinToString
default separator is ", "
.
I copied my default parameters from Kotlin's joinToString
but I guess it's better to go with Array.prototype.join
in this case. more familiar API for JS developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I'm against this API as it stands right now since
array.map(selector).join(separator)
is more idiomatic and faster.
I agree with @kt3k that we should include limit
and truncated
for this to be included in std.
I want to note though @kt3k, that the current version should already be faster than .map().join(), which is a good reason in itself.
@LionC Do you have any source for your claim?
With an array of length 500
with { num: Math.random() * 100 }
as element and obj => "" + Math.floor(obj.num)
as selector, current implementation takes 500ms while map-join takes 150ms.
collections/join_to_string.ts
Outdated
let ret = prefix; | ||
|
||
array.forEach((it, index) => { | ||
if (index > 0) { | ||
ret += separator; | ||
} | ||
|
||
ret += selector(it); | ||
}); | ||
|
||
ret += suffix; | ||
|
||
return ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let ret = prefix; | |
array.forEach((it, index) => { | |
if (index > 0) { | |
ret += separator; | |
} | |
ret += selector(it); | |
}); | |
ret += suffix; | |
return ret; | |
return `${prefix ?? ""}${array.map(selector).join(separator)}${suffix ?? ""}}` |
- User might pass
null
forprefix
andsuffix
, I don't think they wantnull
literally in the string. - While string concatenation is heavily optimised in v8, I think map is more idiomatic and faster. When we introduce
limit
andtruncated
, we should still use an intermediate array instead of doing string concatenation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User might pass null for prefix and suffix, I don't think they want null literally in the string.
i guess passing null
for prefix
or suffix
throw Type 'null' is not assignable to type 'string'.
error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only if you use TypeScript with type checking enabled 😉. With the core team's push on removing type check by default for TypeScript, I think it might be worthy to start adding these trivial type guards. Feel free to discard that suggestion through, I don't have too strong of an opinion on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am very strongly against introducing special code to protect JS consumers from passing the wrong types - that is what TS is for.
collections/join_to_string.ts
Outdated
array: readonly T[], | ||
selector: (el: T) => string, | ||
{ | ||
separator = ", ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separator = ", ", | |
separator = ",", |
Agree with @LionC here, I think it's better if it match Array.prototype.join
with a single comma without space.
@kt3k review, please. |
limit?: number; | ||
truncated?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a fan of that type, as it allows to set truncated
without setting limit
, which does not make sense. I think this should be
limit?: number; | |
truncated?: string; | |
truncate?: { | |
limit: number; | |
truncation?: string; | |
}; |
or something similar to leverage the compiler catching wrong usage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we use your suggested type:
export function joinToString<T>(
array: readonly T[],
selector: (el: T) => string,
{
separator = ",",
prefix = "",
suffix = "",
truncate = {
truncated: "...",
limit: -1,
}
}: Readonly<JoinToStringOptions>,
)
{
truncated: "...",
limit: -1,
}
this is the default value for the truncate
option. what if the user overrides this with only limit
since the truncated
suppose to be optional. the truncated
would be undefined
.
joinToString(someArray, {
truncate: {
limit: 3
}
});
I know I can handle the default values in the function body but I guess the code would be a little bit messy and I don't know if we want to go that way. the implemented type is just like the Kotlin version of this method so I guess its some what okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @majidsajadi . Let's keep the implementation simple. I think it's mostly obvious that truncated is only meaningful with limit
set in this particular case.
let result = ""; | ||
|
||
let index = -1; | ||
while (++index < array.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: I am personally not the biggest fan of implicit mutation with increment operators, as it gets missed quite easily when reading the code, hiding the core step of the loop. I always find it a lot cleaner to just have a separate
index += 1
line
let result = ""; | ||
|
||
let index = -1; | ||
while (++index < array.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not a for
loop? Or a do .. while
loop? The index
initialisation with -1
looks odd to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i need to access the index of the element. i cant use forEach since I need to have the option to break the loop.
i know we can use array.entries
but I thought it would affect the performance (maybe? I'm not sure.)
I saw this pattern (using while for loop through the arrays) in lodash a lot. so I used that here too. but I'm open to any suggestion that makes the code more clear.
result = prefix + result + suffix; | ||
|
||
return result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Could be shortened to directly return
:
result = prefix + result + suffix; | |
return result; | |
return `${prefix}${result}${suffix}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @majidsajadi for your contribution!
LGTM
@majidsajadi Sorry we changed the CLA a little bit (changed the contact email address), and we need you to sign it again.. |
@kt3k Done. |
Thanks! |
No description provided.