Determine if a string has all unique characters: Difference between revisions

→‎{{header|JavaScript}}: added a JS draft.
(→‎{{header|JavaScript}}: added a JS draft.)
Line 494:
"XYZ ZYX" (7) -> 'X' (0x58) at 0, 6
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (36) -> '0' (0x30) at 9, 24</pre>
 
=={{header|JavaScript}}==
<lang javascript>(() => {
'use strict';
 
// duplicatedCharIndices :: String -> Maybe (Char, [Int])
const duplicatedCharIndices = s => {
const go = xs =>
1 < xs.length ? (() => {
const
rest = xs.slice(1),
[c, i] = Array.from(xs[0]);
 
return maybe(go(rest))(
ci => Just(Tuple(fst(ci))([i, snd(ci)]))
)(find(compose(eq(c), fst))(rest))
})() : Nothing();
return go(
zip(chars(s))(enumFrom(0))
);
};
 
// ------------------------TEST------------------------
const main = () =>
console.log(
fTable('First duplicated character, if any:')(
s => `'${s}'`
)(maybe('None')(tpl => {
const [c, ixs] = Array.from(tpl);
return `'${c}' (0x${showHex(ord(c))}) at ${ixs.join(', ')}`
}))(duplicatedCharIndices)([
"", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
])
);
 
 
// -----------------GENERIC FUNCTIONS------------------
 
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
 
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a => b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
 
// chars :: String -> [Char]
const chars = s => s.split('');
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
x => fs.reduceRight((a, f) => f(a), x);
 
// enumFrom :: Enum a => a -> [a]
function* enumFrom(x) {
let v = x;
while (true) {
yield v;
v = 1 + v;
}
}
 
// eq (==) :: Eq a => a -> a -> Bool
const eq = a => b => a === b;
 
// find :: (a -> Bool) -> [a] -> Maybe a
const find = p => xs => {
const i = xs.findIndex(p);
return -1 !== i ? (
Just(xs[i])
) : Nothing();
};
 
// fTable :: String -> (a -> String) -> (b -> String)
// -> (a -> b) -> [a] -> String
const fTable = s => xShow => fxShow => f => xs => {
// Heading -> x display function ->
// fx display function ->
// f -> values -> tabular string
const
ys = xs.map(xShow),
w = Math.max(...ys.map(length));
return s + '\n' + zipWith(
a => b => a.padStart(w, ' ') + ' -> ' + b
)(ys)(
xs.map(x => fxShow(f(x)))
).join('\n');
};
 
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
 
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite length.
// This enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
 
// maybe :: b -> (a -> b) -> Maybe a -> b
const maybe = v =>
// Default value (v) if m is Nothing, or f(m.Just)
f => m => m.Nothing ? v : f(m.Just);
 
// ord :: Char -> Int
const ord = c => c.codePointAt(0);
 
// showHex :: Int -> String
const showHex = n =>
n.toString(16);
 
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
 
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n => xs =>
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
 
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs => ys => {
const
lng = Math.min(length(xs), length(xs)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => Tuple(x)(vs[i]));
};
 
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
xs => ys => {
const
lng = Math.min(length(xs), length(ys)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => f(x)(vs[i]));
};
 
// MAIN ---
return main();
})();</lang>
{{Out}}
<pre>First duplicated character, if any:
'' -> None
'.' -> None
'abcABC' -> None
'XYZ ZYX' -> 'X' (0x58) at 0, 6
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' -> '0' (0x30) at 9, 24</pre>
 
=={{header|Julia}}==
9,659

edits