Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicating an array's elements using functional programming

I'm trying to duplicate each element in an array, but using functional style.

I have this currently:

["a", "b", "c"]

And I'm getting this:

["a","a","b","b","c","c"]

So far I have tried the following, mapping each element to an array, then using flat() to get a 1d array. Is there a cleaner way because it feels like I'm abusing map and flat.

["a", "b", "c"].map(item => [item, item]).flat();

Is there a better way to do this?


I was trying to provide a example as simple as possible but left some details out. The real input is not sorted because elements are not comparable. It's something like:

[
  {
    a:"a"
    b:"b"
  },
  {
    c: 1
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
]

The duplicated result should be something like this, where duplicated elements are next to each other:

[
  {
    a:"a"
    b:"b"
  },
  {
    a:"a"
    b:"b"
  },
  {
    c: 1
    d: 2
  },
  {
    c: 1
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
]
like image 383
uylmz Avatar asked Nov 11 '18 01:11

uylmz


People also ask

How do you duplicate an array?

To duplicate an array, just return the element in your map call. numbers = [1, 2, 3]; numbersCopy = numbers. map((x) => x); If you'd like to be a bit more mathematical, (x) => x is called identity.

How do I copy an element from one array to another in JavaScript?

“concat()” is another useful JavaScript method that can assist you in copying array elements. In the concat() method, you can take an empty array and copy the original array elements to it. It will create a fresh copy of the specified array. var array2 = [].


4 Answers

Array.reduce is semantically the appropriate method here: take an object (in this case an array) and return an object of a different type, or with a different length or shape (note: edited to use Array.push for faster performance per @slider suggestion):

EDIT: I've edited my answer to reflect OP's updated input data. Note also, that this solution is cross-browser and NodeJS compatible without requiring transpilation.

let data = [
  {
    a:"a",
    b:"b",
  },
  {
    c: 1,
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
];

let result = data
  .reduce((acc, el) => {
    acc.push(el, el);
    return acc;
  }, []);
  
console.log(JSON.stringify(result, null, 2));

Otherwise you could map each element, duplicating it, then combine them:

let data = [
  {
    a:"a",
    b:"b",
  },
  {
    c: 1,
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
];

let result = data.map(item => [item, item]).reduce((acc, arr) => acc.concat(arr));

console.log(JSON.stringify(result, null, 2));

As mentioned in other answers here, either of these approaches have the advantage of not requiring the original array to have been sorted.

like image 128
Matt Morgan Avatar answered Oct 17 '22 21:10

Matt Morgan


You can use the function reduce and concatenate the same object on each iteration.

let array = ["a", "b", "c"],
    result = array.reduce((a, c) => a.concat(c, c), []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 44
Ele Avatar answered Oct 17 '22 19:10

Ele


I would recommend Array.prototype.flatMap -

const twice = x =>
  [ x, x ]
  
console .log
  ( [ 'a', 'b', 'c' ] .flatMap (twice) // [ 'a', 'a', 'b', 'b', 'c', 'c' ]
  , [ 1, 2, 3, 4, 5 ] .flatMap (twice) // [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]
  )

flatMap is useful for all kinds of things -

const tree =
  [ 0, [ 1 ], [ 2, [ 3 ], [ 4, [ 5 ] ] ] ]
  
const all = ([ value, ...children ]) =>
  [ value ] .concat (children .flatMap (all))
  
console .log (all (tree))
// [ 0, 1, 2, 3, 4, 5 ]

really cool things -

const ranks =
  [ 'J', 'Q', 'K', 'A' ]
  
const suits =
  [ '♡', '♢', '♤', '♧' ]

console .log
  ( ranks .flatMap (r =>
      suits .flatMap (s =>
        [ [ r, s ] ]
      )
    )
  )

// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]

flatMap is just a specialised Array.prototype.reduce and is easy to implement in environments where Array.prototype.flatMap is not already supported -

const identity = x =>
  x

const flatMap = (xs = [], f = identity) =>
  xs .reduce ((r, x) => r . concat (f (x)), [])

const ranks =
  [ 'J', 'Q', 'K', 'A' ]

const suits =
  [ '♡', '♢', '♤', '♧' ]

console.log
  ( flatMap (ranks, r =>
      flatMap (suits, s =>
        [ [ r, s ] ]
      )
    )
  )

// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]
like image 4
Mulan Avatar answered Oct 17 '22 21:10

Mulan


You could just do this:

var arr = ["a", "b", "c"];
arr = arr.concat(arr).sort();

This is one of the simplest methods to do what you are asking to do.

like image 2
Jack Bashford Avatar answered Oct 17 '22 20:10

Jack Bashford