0

how to check the duplicate number/string in arrays with Recursion, then push them into a newArr , and push the unique number/string to newArrTwo

function rec(those) {
 let [unique, twin] = [ [], [] ];
 
};

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

the output i want is unique = ["arr", "obj", "arg"]; and twin = [2,"str"]

Zr Classic
  • 303
  • 4
  • 14
  • 2
    If you want somebody to do a homework for you at least explain it properly. – zerkms Dec 20 '18 at 01:55
  • You've put a huge effort into this man! despite having no code at all, at least show us how would you expect them to output? – mrReiha Dec 20 '18 at 01:56
  • this is not a homework (REALLY), this is for known how to do it by recursion, @zerkms – Zr Classic Dec 20 '18 at 01:57
  • 1
    "how to do it" --- what _it_? Read your question and answer honestly, do you seriously think it's possible to understand what you mean? – zerkms Dec 20 '18 at 01:58
  • 1
    Remember that we expect you to do your own work here, and then post when you have given it some real work and some research and are still stuck. So... what have you tried so far? – Scott Sauyet Dec 20 '18 at 02:00
  • It's not really clear why you want to use recursion for this. It seems like there's no benefit and it would make the solution needlessly more difficult to understand. – Mark Dec 20 '18 at 02:40
  • @MarkMeyer: I disagree. Recursive solutions very often offer much cleaner code. I have a solution (actually a few of them) that I would like to share, but only when the OP can show some actual work. – Scott Sauyet Dec 20 '18 at 02:46
  • I hope they do, I'd like to see what you have in mind @ScottSauyet. – Mark Dec 20 '18 at 02:49
  • @ZrClassic: So... do you have anything to show us of what you did? – Scott Sauyet Dec 20 '18 at 02:49
  • Heck with it. Posting my solution. But @ZrClassic, please remember in the future that we expect you to put in real effort on your own questions here. – Scott Sauyet Dec 20 '18 at 03:00
  • For nested structures recursion often offers cleaner code, but for flat structures like array in OP question usually not (so this is not good example to learn recursion). – Kamil Kiełczewski Dec 20 '18 at 05:24
  • @KamilKiełczewski: Perhaps, but see my answer, which conceives of any non-empty array as `[x, ...xs]`. This recursive structure makes a recursive answer quite palatable. If we get real pattern matching and TCO, I will expect this to become *the way* to do such work on arrays. – Scott Sauyet Dec 20 '18 at 14:17
  • @ScottSauyet - yes your code looks nice, however still I think this kind of problems are not good examples to learn/use recursive function because they not shows power of recursive approach. – Kamil Kiełczewski Dec 20 '18 at 14:21

3 Answers3

2

Update

I misread the specifications originally. Below this section I detail several solutions to a different, but related, problem. Here is a solution to the requested one:

const rec = (arr, uniqs = new Set, dups = new Set, [x, ...xs] = arr) => 
  arr.length == 0
  ? [[...uniqs], [...dups]]
  : uniqs.has(x) || dups.has(x)
    ? rec(xs, (uniqs.delete(x), uniqs), dups.add(x))
    : rec(xs, uniqs.add(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

Note that there were two changes from the final original answer:

  • We switched to a Set for the dups as well, with the changes required by that.

  • This line now deletes from the uniqs Set rather than just passing it along :

    ? rec(xs, (uniqs.delete(x), uniqs), dups.add(x))
    

This actually points to an API issue with Set. It's really useful that add returns the set. It's too bad that delete doesn't also do so. While the boolean return might occasionally be helpful, it's quite easy to get that with has. It's far too late to fix this, but it really is a shame.

Original Answer

Here is one possible approach. ES6 features such as rest parameters and default parameters alongside a Symbol makes for a fairly elegant implementation.

const None = Symbol()

const rec = ([x = None, ...xs], uniqs = [], dups = []) => 
  x == None 
  ? [uniqs, dups]
  : uniqs.includes(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.concat(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

One real help for recursion in modern JS is the ability to use default parameters to allow you to avoid additional helper functions.

The use of the Symbol here is an interesting way to combine our spread of the input array into a head and a tail and still allow us to test if it's empty. An alternative means is shown below.

const rec = (arr, uniqs = [], dups = [], [x, ...xs] = arr) => 
  arr.length == 0 
  ? [uniqs, dups]
  : uniqs.includes(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.concat(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

This version feels slightly less clean than the original, but it does not require defining a helper symbol just to check for emptiness.

There is still something wrong with this; it could have performance problems. Because we have to call the O(n) includes for every element, the total solution is somthing like O(n^2), depending on the ration of unique values to duplicates. This might not be a problem, and I would only fix it in one of two scenarios:

  • I've tested and found that this code is an actual bottleneck in my application
  • I have a more performant alternative version that sacrifices little code clarity.

In this case, I do have such a version:

const rec = (arr, uniqs = new Set(), dups = [], [x, ...xs] = arr) => 
  arr.length == 0
  ? [[...uniqs], dups]
  : uniqs.has(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.add(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

(We could also use the Symbol alternative in this version.)

Here, we switch to using a Set, which is precisely the data type designed for working with a collection of unique elements. That necessitates doing a little unwrapping of the set, with [...uniqs] -- alternatively Array.from(uniqs) -- but that is a very minor increase in complexity. The other changes just have to do with switching from an array to a set.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
1

My attempt:

function rec(array) {

  const result = array.reduce((result, current) => {
    if(result[0].indexOf(current) == -1) {
      result[0].push(current);
    }
    else {
      result[1].push(current);
    }
    return result;
  }, [[], []]);


  result[0] = result[0].filter((elem) => result[1].indexOf(elem) === -1);

  return result;

}
explorer
  • 944
  • 8
  • 18
0

Try this (explanation here):

 let twin = those.filter(( t={}, e=>!(1-(t[e]=++t[e]|0)) )); 
 let unique = those.filter(x=>!twin.includes(x)); 

function rec(those) {
 let t={};
 let twin = those.filter(( t={}, e=>!(1-(t[e]=++t[e]|0)) )); 
 let unique = those.filter(x=>!twin.includes(x)); 
  
 return [unique, twin];
};

console.log(JSON.stringify(rec([2, "str", "arr", "str", 2, "obj", "arg"])));
console.log(JSON.stringify(rec(["ref", "val", "val", "val", "el", "el"])));
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
  • Note that the question (reiterated by the comments) specifically looked for a recursive version of this function. I'm assuming that's as a way to learn more about recursion. There are many non-recursive ways of solving this. – Scott Sauyet Dec 20 '18 at 04:02