How to push different combos of elements into a new array? - javascript

The question I am trying to answer is: Combos of Any length
Modify this function so that it returns all combinations of the elements of arr as an array of arrays. Use Recursion!
This is what I have so far:
function getAllCombos(arr, newArr = []) {
if (arr[0] === undefined) return newArr
newArr.push(arr)
return getAllCombos(arr.slice(1), newArr)
}
console.log(getAllCombos(['a', 'b']));
console.log(getAllCombos(['a', 'b', 'c']))
it gives me this result:
[['a', 'b'], ['b']]
[['a', 'b', 'c'], ['b', 'c'], ['c']]
I am looking to get this result:
[['a','b'], ['a'], ['b'], []]
[['a', 'b', 'c'],['a', 'b'], ['a', 'c'], ['a'], ['b', 'c'], ['b'], ['c'], [],]
How do I iterate through the array getting different combos? While also not repeating arrays that have already been pushed?
Here are the test cases:
console.log(getAllCombos(['a', 'b']));
console.log(getAllCombos(['a', 'b', 'c']))

Short answer
const getAllCombosShort= array => array.reduce((acc, curr) => acc.flatMap(e => [e, [...e, curr]]), [[]])
console.log(getAllCombosShort(['a','b','c']));
Detailed answer
How recursion is structured
Recursion implies structuring your solution in three steps:
Base Case: All the combos of an empty array is simply an array with an empty array inside.
Induction Hypothesis (I.H.): Suppose it's known how to get all the combos for any array of length n-1.
General Case (G.C.): Proof that you can solve for any array of length n using previous step.
How to construct a recursive algorithm
The key to reach the solution S through recursion is to find out how to extend the truth from I.H. to G.C.
Assume that you give some arbitrary input to one of your best friends and, in return, he gives you the solution S' for the problem. However, as he doesn't want to give the solution S to you on a silver plate, he accepted to do all the part of the work which have one unit less than the original input.
With his solution S' in hands you're not only able to solve the problem with the complete input, but also construct the algorithm naturally.
For instance, ['a','b', 'c']
Give him ['b', 'c'] and get:
[ ['b','c'], ['b'], ['c'], [] ]
For each element of it, create a new element which contains itself plus 'a' and push it to his solution array, i.e.:
[ ['b','c'], ['b'], ['c'], [] ,
['a','b','c'], ['a','b'], ['a','c'], ['a'] ]
Pseudocode
algorithm allCombos
input: array of length n
output: array of all combinations
if array is empty
return an array with empty inside
head := head(array) -- head
tail := tail(array)
friend := allCombos(tail) -- your friend knows how to solve for one smaller
for each element of friends do
new_elem := [...element, head]
friend.push(new_elem)
return friend
Correspondent in Javascript:
function getAllCombos(array) {
if (array && array.length === 0) return [[]]
let head = array[0]
let tail = array.slice(1)
friend = getAllCombos(tail)
friend.forEach(elem => friend.push([head, ...elem]))
return friend
}
console.log(getAllCombos(['a','b']))
console.log(getAllCombos(['a', 'b', 'c']))

Related

Filter javascript multidimensional array for values that exist at every index [duplicate]

This question already has answers here:
Array intersection (set-theoretic) with Array.prototype.reduce
(3 answers)
Closed 1 year ago.
Let's say that I have the following multidimensional array :
const arrayOfValues = [
['a', 'b', 'c'],
['a', 'c'],
['c']
];
I would like to create a new array containing only the values that are present at every index of my original multidimensional array. So I would end up with the following array in this case :
['c']
I'm having trouble finding a way to do so as I am not too familiar with javascript. I'm guessing I have to apply some sort of filtering (probably map over the array and apply a specific condition) but I'm not sure how to go about it.
You can use Array#reduce along with Array#filter.
const arr = [
['a', 'b', 'c'],
['a', 'c'],
['c']
];
let res = arr.reduce((acc,curr)=>acc.filter(x=>curr.includes(x)));
console.log(res);
Start with a persistent variable that starts with the values in the first subarray. Then iterate over each other subarray, reassigning that variable to a filtered version of the subarray currently being iterated over, filtering by whether the item is contained both in the previous subarray and the current one:
const arrayOfValues = [
['a', 'b', 'c'],
['a', 'c'],
['c']
];
let previous = arrayOfValues[0];
arrayOfValues.slice(1).forEach((subarr) => {
previous = subarr.filter(
item => previous.includes(item)
);
});
console.log(previous);

Rotate the elements of an array

I am trying to solve a javascript challenge from jshero.net. The challenge is this:
Write a function rotate that rotates the elements of an array. All
elements should be moved one position to the left. The 0th element
should be placed at the end of the array. The rotated array should be
returned. rotate(['a', 'b', 'c']) should return ['b', 'c', 'a'].
All I could come up with was this :
function rotate(a){
let myPush = a.push();
let myShift = a.shift(myPush);
let myFinalS = [myPush, myShift]
return myFinalS
}
But the error message I got was:
rotate(['a', 'b', 'c']) does not return [ 'b', 'c', 'a' ], but [ 3,
'a' ]. Test-Error! Correct the error and re-run the tests!
I feel like I'm missing something really simple but I can't figure out what. Do you guys have other ways to solve this?
function rotate(array){
let firstElement = array.shift();
array.push(firstElement);
return array;
}
To achieve the output you are looking for, first you have to use Array.shift() to remove the first element, then using Array.push() add the element back to the end of the Array, then return the array, the issue is that you used the wrong oder for these steps, also .push() method takes element to be added as argument, here is a working snippet:
function rotate(a){
let myShift = a.shift();
a.push(myShift);
return a;
}
console.log(rotate(['a', 'b', 'c']));
Here I have created a utility where, the input array will not get mutated even after rotating the array as per the requirement.
function rotate(a){
let inputCopy = [...a]
let myShift = inputCopy.shift();
let myFinalS = [...inputCopy, myShift]
return myFinalS
}
console.log(rotate([1,2,3]))
console.log(rotate(["a","b","c"]))
Hope this helps.
function rotate(arr){
let toBeLast = arr[0];
arr.splice(0, 1);
arr.push(toBeLast);
return arr;
}
console.log(rotate(['a', 'b', 'c']));
New to stack overflow. Hope this helps :)
arr.unshift(...arr.splice(arr.indexOf(k)))
Using unshift(), splice() and indexOf(), this is a one line that should help. arr is the array you want to rotate and k the item you want as first element of the array. An example of function could be:
let rotate = function(k, arr) {
arr.unshift(...arr.splice(arr.indexOf(k)))
}
And this are examples of usage:
let array = ['a', 'b', 'c', 'd']
let item = 'c'
rotate(item, array)
console.log(array)
// > Array ["c", "d", "a", "b"]
Finally back to the original array:
rotate('a', array)
console.log(array)
// > Array ["a", "b", "c", "d"]

How to get the even and odd entries from an array with Ramda

I have the following:
var isEven = function (n) { return n % 2 === 0; }
var isOdd = function (n) { return n % 2 !== 0; }
var indexedList = function(fn, list) {
var array = [];
for (var i = 0; i < list.length; i++) {
if (fn(i)) {
array.push(list[i]);
}
}
return array;
}
Is there a Ramda equivalent of IndexedList so I can have an array of just the even index based elements and an array of odd based index elements.
Ramda's list-based functions by default do not deal with indices. This, in part, is because many of them are more generic and also work with other data structures where indices don't make sense. But there is a standard mechanism for altering functions so that they do pass the indices of your lists along: addIndex.
So my first thought on this is to first of all, take your isEven and extend it to
var indexEven = (val, idx) => isEven(idx);
Then you can use addIndex with filter and reject like this:
R.addIndex(R.filter)(indexEven, ['a', 'b', 'c', 'd', 'e']);
//=> ['a', 'c', 'e']
R.addIndex(R.reject)(indexEven, ['a', 'b', 'c', 'd', 'e']);
//=> ['b', 'd']
Or if you want them both at once, you can use it with partition like this:
R.addIndex(R.partition)(indexEven, ['a', 'b', 'c', 'd', 'e']);
//=> [["a", "c", "e"], ["b", "d"]]
You can see this in action, if you like, on the Ramda REPL.
If the list length is even, I would go with
R.pluck(0, R.splitEvery(2, ['a','b','c']))
The disadvantage of this is that it will give undefined as a last element, when list length is odd and we want to select with offset 1 ( R.pluck(1) ). The advantage is that you can easily select every nth with any offset while offset < n.
If you can't live with this undefined than there is another solution that I find more satisfying than accepted answer, as it doesn't require defining a custom function. It won't partition it nicely though, as the accepted answer does.
For even:
R.chain(R.head, R.splitEvery(2, ['a','b','c','d']))
For odd:
R.chain(R.last, R.splitEvery(2, ['a','b','c','d']))
As of Ramda 0.25.0, the accepted solution will not work. Use this:
const splitEvenOdd = R.compose(R.values, R.addIndex(R.groupBy)((val,idx) => idx % 2))
splitEvenOdd(['a','b','c','d','e'])
// => [ [ 'a', 'c', 'e' ], [ 'b', 'd' ] ]

Using lodash to compare jagged arrays (items existence without order)

I know I can do it using loops, but I'm trying to find an elegant way of doing this:
I have two jagged arrays (array of arrays):
var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
I want to use lodash to confirm that the above two jagged arrays are the same. By 'the same' I mean that there is no item in array1 that is not contained in array2. Notice that the items in jagged array are actually arrays. So I want to compare between inner arrays.
In terms of checking equality between these items:
['a', 'b'] == ['b', 'a']
or
['a', 'b'] == ['a', 'b']
Both work since the letters will always be in order.
UPDATE: Original question was talking about to "arrays" (instead of jagged arrays) and for years many people discussed (and added answers) about comparing simple one-dimensional arrays (without noticing that the examples provided in the question were not actually similar to the simple one-dimensional arrays they were expecting).
If you sort the outer array, you can use _.isEqual() since the inner array is already sorted.
var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isEqual(array1.sort(), array2.sort()); //true
Note that .sort() will mutate the arrays. If that's a problem for you, make a copy first using (for example) .slice() or the spread operator (...).
Or, do as Daniel Budick recommends in a comment below:
_.isEqual(_.sortBy(array1), _.sortBy(array2))
Lodash's sortBy() will not mutate the array.
You can use lodashs xor for this
doArraysContainSameElements = _.xor(arr1, arr2).length === 0
If you consider array [1, 1] to be different than array [1] then you may improve performance a bit like so:
doArraysContainSameElements = arr1.length === arr2.length && _.xor(arr1, arr2).length === 0
There are already answers here, but here's my pure JS implementation. I'm not sure if it's optimal, but it sure is transparent, readable, and simple.
// Does array a contain elements of array b?
const union = new Set([...a, ...b]);
const contains = (a, b) => union.size === a.length && union.size === b.length;
// Since order is not important, just data validity.
const isEqualSet = (a, b) => union.contains(a, b) || union.contains(b, a)
The rationale in contains() is that if a does contain all the elements of b, then putting them into the same set would not change the size.
For example, if const a = [1,2,3,4] and const b = [1,2], then new Set([...a, ...b]) === {1,2,3,4}. As you can see, the resulting set has the same elements as a.
From there, to make it more concise, we can boil it down to the following:
const isEqualSet = (a: string[], b: sting[]): boolean => {
const union = new Set([...a, ...b])
return union.size === a.length && union.size === b.length;
}
Edit: This will not work with obj[{a: true}, true, 3] but does compare array contents probably as long as they are primitive elements. Method fixed and tested against strings two arrays using the same values in different orders. Does not work with object types. I recommend making a universal helper which calls a helper function depending on the type which needs to be compared. Try _.isEqual(a. b); from the very fantastic lodash library.
By 'the same' I mean that there are is no item in array1 that is not contained in array2.
You could use flatten() and difference() for this, which works well if you don't care if there are items in array2 that aren't in array1. It sounds like you're asking is array1 a subset of array2?
var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
function isSubset(source, target) {
return !_.difference(_.flatten(source), _.flatten(target)).length;
}
isSubset(array1, array2); // → true
array1.push('d');
isSubset(array1, array2); // → false
isSubset(array2, array1); // → true
PURE JS (works also when arrays and subarrays has more than 2 elements with arbitrary order). If strings contains , use as join('-') parametr character (can be utf) which is not used in strings
array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join()
var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['b', 'a']];
var r = array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join();
console.log(r);
I definitely feel very unclean for posting this solution, but:
var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isMatch([array1], [array2]) && _.isMatch([array2], [array1]) // true
array1 = [['b', 'a'], ['c', 'b']];
array2 = [['b', 'c'], ['a', 'b']];
_.isMatch([array1], [array2]) && _.isMatch([array2], [array1]) // also true
Note that you have to wrap array1 and array2 into a container (array, object) in order for this to work? Why? There's probably a perfectly stupid reason for this.
import { differenceBy } from 'lodash'
export default function (arr1, arr2) {
return !differenceBy(arr1, arr2).length && arr1.length === arr2.length
}
if there are no different characters and the array length is the same, it makes them the same.
Edit: I missed the multi-dimensional aspect of this question, so I'm leaving this here in case it helps people compare one-dimensional arrays
It's an old question, but I was having issues with the speed of using .sort() or sortBy(), so I used this instead:
function arraysContainSameStrings(array1: string[], array2: string[]): boolean {
return (
array1.length === array2.length &&
array1.every((str) => array2.includes(str)) &&
array2.every((str) => array1.includes(str))
)
}
It was intended to fail fast, and for my purposes works fine.
We can use _.difference function to see if there is any difference or not.
function isSame(arrayOne, arrayTwo) {
var a = _.uniq(arrayOne),
b = _.uniq(arrayTwo);
return a.length === b.length &&
_.isEmpty(_.difference(b.sort(), a.sort()));
}
// examples
console.log(isSame([1, 2, 3], [1, 2, 3])); // true
console.log(isSame([1, 2, 4], [1, 2, 3])); // false
console.log(isSame([1, 2], [2, 3, 1])); // false
console.log(isSame([2, 3, 1], [1, 2])); // false
// Test cases pointed by Mariano Desanze, Thanks.
console.log(isSame([1, 2, 3], [1, 2, 2])); // false
console.log(isSame([1, 2, 2], [1, 2, 2])); // true
console.log(isSame([1, 2, 2], [1, 2, 3])); // false
I hope this will help you.
Adding example link at StackBlitz

Replace in array using lodash

Is there an easy way to replace all appearances of an primitive in an array with another one. So that ['a', 'b', 'a', 'c'] would become ['x', 'b', 'x', 'c'] when replacing a with x. I'm aware that this can be done with a map function, but I wonder if have overlooked a simpler way.
In the specific case of strings your example has, you can do it natively with:
myArr.join(",").replace(/a/g,"x").split(",");
Where "," is some string that doesn't appear in the array.
That said, I don't see the issue with a _.map - it sounds like the better approach since this is in fact what you're doing. You're mapping the array to itself with the value replaced.
_.map(myArr,function(el){
return (el==='a') ? 'x' : el;
})
I don't know about "simpler", but you can make it reusable
function swap(ref, replacement, input) {
return (ref === input) ? replacement : input;
}
var a = ['a', 'b', 'a', 'c'];
_.map(a, _.partial(swap, 'a', 'x'));
If the array contains mutable objects, It's straightforward with lodash's find function.
var arr = [{'a':'a'}, {'b':'b'},{'a':'a'},{'c':'c'}];
while(_.find(arr, {'a':'a'})){
(_.find(arr, {'a':'a'})).a = 'x';
}
console.log(arr); // [{'a':'x'}, {'b':'b'},{'a':'x'},{'c':'c'}]
Another simple solution. Works well with arrays of strings, replaces all the occurrences, reads well.
var arr1 = ['a', 'b', 'a', 'c'];
var arr2 = _.map(arr1, _.partial(_.replace, _, 'a', 'd'));
console.log(arr2); // ["d", "b", "d", "c"]

Categories

Resources