Related
Input:
[1,2,[3,4,[5,6]]]
Output:
[[1,2],[3,4],[5,6]]
Here is the solution:
function convert(a,res=[]) {
const group = (arr) => {
res.push(arr.slice(0,2));
arr.map((v) => Array.isArray(v) && group(v));
}
group(a);
return res;
}
console.log(convert([1,2,[3,4]])); // [[1,2],[3,4]]
console.log(convert([1,2,[3,4,[5,6]]])); // [[1,2],[3,4],[5,6]]
console.log(convert([1,2,[3,4,[5,6,[7,8]]]])); // [[1,2],[3,4],[5,6],[7,8]];
Although the problem is solved, For learning purposes, I've always wonder how to solve this without the nesting function approach. I've tried to refactor the code as followed:
function convert(a,i=0,res=[]) {
return i >= a.length
? res
: convert(
a,
i+1,
Array.isArray(a[i]) ? [...res,a.slice(0,2)] : res
)
}
console.log(convert([1,2,[3,4]])); // [[1,2]]
console.log(convert([1,2,[3,4,[5,6]]])); // [[1,2]]
console.log(convert([1,2,[3,4,[5,6,[7,8]]]])); // [[1,2]]
As you can see, the result is less than ideal. I just couldn't quite wrap my head around it. Any feedback and pointer will be greatly appreciated :)
UPDATE:
Here is the solution which covers even more test cases:
function convert(a,res=[]) {
return !a.length
? res
: convert(
a.filter(Array.isArray).flat(),
[...res,a.filter((v) => !Array.isArray(v))]
);
}
console.log(convert([1,2,[3,4]])); // [[1,2],[3,4]]
console.log(convert([1,2,[3,4,[5,6]]])); // [[1,2],[3,4],[5,6]]
console.log(convert([1,2,[3,4,[5,6,[7,8]]]])); // [[1,2],[3,4],[5,6],[7,8]];
console.log(convert([1,2,[5,6,[9,10],7,8],3,4])); // [[1,2,3,4],[5,6,7,8],[9,10]]
console.log(convert([1,5,5,[5,[1,2,1,1],5,5],5,[5]])); // [[1,5,5,5],[5,5,5,5],[1,2,1,1]]
console.log(convert([1,[2],1,[[2]],1,[[[2]]],1,[[[[2]]]]])); // [[1,1,1,1],[2],[2],[2],[2]]
I'm not sure if this is what you mean, but you can use a straight recursive call, spreading each result into the previous.
const input = [1, 2, [3, 4, [5, 6]]];
function flatten(arr) {
const res = [[]];
for (const e of arr) {
Array.isArray(e) ? res.push(...flatten(e)) : res[0].push(e);
}
return res;
}
console.log(flatten(input));
This works even if the elements on any level are split by nested levels, and for variable numbers of elements on any given level.
const input = [1, 2, [4, [5, 6, [8, 9], 7]], 3];
// [[ 1, 2, 3 ], [ 4 ], [ 5, 6, 7 ], [ 8, 9 ]]
function flatten(arr) {
const res = [[]];
for (const e of arr) {
Array.isArray(e) ? res.push(...flatten(e)) : res[0].push(e);
}
return res;
}
console.log(flatten(input));
Edit
To accommodate the added conditions brought up in the comments, but maintain straight recursion without passing an accumulator to later calls, I might do something like the following.
function flatten(arr) {
const level = [], nested = [];
for (const e of arr) {
Array.isArray(e) ? nested.push(...e) : level.push(e);
}
return [level, ...(nested.length ? flatten(nested) : [])]
}
console.log(flatten([1, 2, [3, 4, [5, 6]]]));
// [[ 1, 2 ], [ 3, 4 ], [ 5, 6 ]]
console.log(flatten([1, 2, [4, [5, 6, [8, 9], 7]], 3]));
// [[ 1, 2, 3 ], [ 4 ], [ 5, 6, 7 ], [ 8, 9 ]]
console.log(flatten([1, [2], 1, [[2]], 1, [[[2]]], 1, [[[[2]]]]]));
// [[ 1, 1, 1, 1 ], [ 2 ], [ 2 ], [ 2 ], [ 2 ]]
console.log(flatten([1, 5, 5, [5, [1, 2, 1, 1], 5, 5], 5, [5]]));
// [[ 1, 5, 5, 5 ], [ 5, 5, 5, 5 ], [ 1, 2, 1, 1 ]]
I think your updated algorithm is fine.
It might be easier to express with a reusable utility function, though. I usually have a partition function lying around which accepts a predicate function and returns a function which splits an array into two sub-arrays, those for which the predicate returns true, and those for which it's false.
Using that, this becomes fairly simple:
const partition = (pred) => (xs) =>
xs .reduce (([t, f], x) => pred (x) ? [t .concat (x), f]: [t, f .concat (x)], [[], []])
const convert = (xs, [rest, first] = partition (Array .isArray) (xs)) =>
xs .length == 0 ? [] : [first, ...convert (rest)]
console.log (convert ([1, 2, [3, 4]])) //=> [[1, 2], [3, 4]]
console.log (convert ([1, 2, [3, 4, [5, 6]]])) //=> [[1, 2], [3, 4], [5, 6]]
console.log (convert ([1, 2, [3, 4, [5, 6, [7, 8]]]])) //=> [[1, 2], [3, 4], [5, 6], [7, 8]]
console.log (convert ([1, 2, [5, 6, [9, 10], 7, 8], 3, 4])) //=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
console.log (convert ([1, 5, 5, [5, [1, 2, 1, 1], 5, 5], 5, [5]])) //=> [[1, 5, 5, 5], [5, 5, 5, 5], [1, 2, 1, 1]]
console.log (convert ([1, [2], 1, [[2]], 1, [[[2]]], 1, [[[[2]]]]])) //=> [[1, 1, 1, 1], [2], [2], [2], [2]]
.as-console-wrapper {max-height: 100% !important; top: 0}
We have an array of arrays like this:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
There may be duplicate elements in each array and that's fine.
But I'm after a proper solution to remove duplicate elements in each set comparing to lower sets!
So as we have a 0 in the first array and the last array, we should consider the 0 in last one a duplication and remove it...
the desired result would be:
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[12],
[14]
It's a confusing issue for me please help...
You could collect the values in an object with index as value, and filter for values who are at the same index.
const
arrays = [[0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]],
seen = {},
result = arrays.map((array, i) => array.filter(v => (seen[v] ??= i) === i));
result.forEach(a => console.log(...a));
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[4, 4, 5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
]
let filtered = arrays.map((row, i) => {
// concat all previous arrays
let prev = [].concat(...arrays.slice(0, i))
// filter out duplicates from prev arrays
return row.filter(r => !prev.includes(r))
})
console.log(filtered)
We can do this using Array#reduce and maintain a seen Set, which will have all visited numbers from each array.
Once you iterate over an array you push all visited elements in the seen Set, then push a new array filtered by the elements not in the seen Set:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const removeDupsInSibling = (arr) => {
let seen = new Set();
return arr.reduce((acc, a)=> {
const f = a.filter(v => !seen.has(v));
seen = new Set([...seen, ...a]);
acc.push(f);
return acc;
}, []);
}
console.log(removeDupsInSibling(arrays));
There are plenty of inefficient ways to do this, but if you want to do this in O(n), then we can make the observation that what we want to know is "which array a number is in". If we know that, we can run our algorithm in O(n):
for every element e in array at index i:
if index(e) == i:
this is fine
if index(e) < i:
remove this e
So let's just do literally that: we allocate an object to act as our lookup, and then we run through all elements:
const lookup = {};
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const reduced = arrays.map((array, index) => {
// run through the elements in reverse, so that we can
// safely remove bad elements without affecting the loop:
for(let i=array.length-1; i>=0; i--) {
let value = array[i];
let knownIndex = (lookup[value] ??= index);
if (knownIndex < index) {
// removing from "somewhere" in the array
// uses the splice function:
array.splice(i,1);
}
}
return array;
});
console.log(reduced);
For an alternative, where the loop+splice is taken care of using filter, see Nina's answer.
Simple, clean and high performance solution:
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14]
];
const duplicates = {};
const answer = arrays.map( (array, level) => {
return array.filter( el => {
if ( duplicates[el] < level ) {
// return nothing; fine
} else {
duplicates[el] = level;
return el
}
})
});
console.log(JSON.stringify(answer))
here is on-liner and less-readable form:
const d = {}, arrays = [ [0, 1, 2, 3, 4, 4, 4, 4], [5, 6, 7, 8, 9, 10, 11, 11], [2, 7, 10, 12], [0, 7, 10, 14]];
const answer = arrays.map((a,l)=> a.filter(el=> d[el]<l ? 0 : (d[el]=l,el)));
console.log(JSON.stringify(answer))
const arrays = [
[0, 1, 2, 3, 4, 4, 4, 4],
[5, 6, 7, 8, 9, 10, 11, 11],
[2, 7, 10, 12],
[0, 7, 10, 14],
];
const output = arrays.reduce(
({ output, set }, current, i) => {
output[i] = current.filter((num) => !set.has(num));
[...new Set(output[i])].forEach((num) => set.add(num));
return { output, set };
},
{ output: [], set: new Set() }
).output;
console.log(output);
Gets the exact output you want:
[
[
0, 1, 2, 3,
4, 4, 4, 4
],
[
5, 6, 7, 8,
9, 10, 11, 11
],
[ 12 ],
[ 14 ]
]
For example, if your input array is [10, 5, 20], the output should be [2, 3, 1], since 10 is the second largest number, 5 is the third largest, and 20 is the largest.
This is my function:
function rankings(arr){
const result=[];
let newArr=arr.sort((a,b)=>b-a);
for (let i=0;i<arr.length;i++){
for (let j=0;j<newArr.length;j++){
arr[i]===newArr[j]? result.push(j+1): console.log('');
}
}
return(result);
}
Using my function with this array [10,5,20] as my input, my output is [1,2,3] while
rankings([10, 5, 20]); // [2, 3, 1] expected output
rankings([6, 8, 1, 12, 4, 3, 9]); // [4, 3, 7, 1, 5, 6, 2] expected output
You can also try to sort the array, and then match the indexes of the original array.
Edited to cater the case of repeated numbers
var originalArray = [10, 5, 20, 10, 20]
, setArray = [...new Set(originalArray)]
, sortedArray = [...setArray].sort((a, b) => a - b)
, dataIndex = originalArray.map(d => sortedArray.indexOf(d) + 1)
console.log(dataIndex)
You could sort the values and filter the items for avoiding same values with different ranks.
function rankings(array) {
const sorted = [...array]
.sort((a, b) => b - a)
.filter((b, i, { [i - 1]: a }) => a !== b);
return array.map(v => sorted.indexOf(v) + 1);
}
console.log(rankings([10, 5, 20])); // [2, 3, 1]
console.log(rankings([6, 8, 1, 12, 4, 3, 9])); // [4, 3, 7, 1, 5, 6, 2]
console.log(rankings([10, 5, 20, 10, 20]));
I'm trying to solve this problem. Essentially, I have a array of keys, and an array of values within objects, and I want those values to have keys.
Below is my best attempt so far - usually use python so this is a bit confusing for me.
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
var result = [];
for (arr in selection) {
numbers.forEach(function (k, i) {
result[k] = arr[i]
})
};
console.log(result);
The output I'm looking for is like this,
results = [{3:1,4:2,5:3,6:4}, {..},..]
Love some pointers to getting the right output.
Note. This is for google appscript! So can't use certain javascript functions (MAP I think doesn't work, unsure of reduce).
Cheers!
Use map on selection and Object.assign
var numbers = [3, 4, 5, 6];
var selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4]
];
var result = selection.map(arr =>
Object.assign({}, ...arr.map((x, i) => ({ [numbers[i]]: x })))
);
console.log(result);
Create a separate function which take keys and values as arguments and convert it into object using reduce(). Then apply map() on selections and make an object for each subarray using that function
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
function makeObject(keys, values){
return keys.reduce((obj, key, i) => ({...obj, [key]: values[i]}),{});
}
const res = selection.map(x => makeObject(numbers, x));
console.log(res)
Create a new object from scratch for each number array:
const selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4],
];
function objMaker(numarr) {
const numbers = [3, 4, 5, 6];
numarr.forEach((num, i) => (this[numbers[i]] = num));
}
console.info(selection.map(numarr => new objMaker(numarr)));
I am trying to solve this freecodecamp algorithm question where I had to collect the difference of two or more arrays. I used map to get the difference of array but the problem is I only get two elements;
function sym(args) {
args = [].slice.call(arguments);
var newArr = args.map(function(el, index, arr){
console.log(arr.indexOf(arr[index]));
if(arr.indexOf(arr[index] === -1 )){
// console.log(arr[index]);
return args.push(arr[index]);
}
});
return newArr; // my newArr returns [3, 4] instead of [3,4,5]
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
//sym([1, 2, 3], [5, 2, 1, 4]) should return [3, 4, 5]
//sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]) should return [1, 2, 4, 5, 6, 7, 8, 9]
I think we could do also this way since we want them to be ordered at the end.
For more detail about the original problem please consult this link: FreecodeCamp Link: Symmetric Difference
const sym = (...args) => {
// Merge all the different arrays and remove duplicate elements it means elements that are present both on two related arrays
let tab = args.reduce((a, b) => [
...a.filter(i => !b.includes(i)),
...b.filter(j => !a.includes(j))
], []);
// Then remove the rest of duplicated values and sort the obtained array
return Array.from(new Set(tab)).sort((a, b) => a - b);
}
console.log(sym([1, 2, 3, 3], [5, 2, 1, 4])); // [3, 4, 5]
console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5])); // [1, 4, 5]
console.log(sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1])); // [1, 2, 4, 5, 6, 7, 8, 9]
The Set data structure is used here to remove duplicated values thanks to its characteristics.
Well your function is a little more complex than only selecting the unique values, cause you want to filter them out... and also accept multiple arrays. This should work.
var sym = (...arrays)=>{
//Concat Items
const allItems = arrays.reduce((a,c)=>a.concat(c), []);
// Identify repeated items
const repeatedItems = allItems.filter((v,i,a)=>a.indexOf(v) !== i);
// Filter repeated items out
const diff = allItems.filter(item=>repeatedItems.indexOf(item) < 0);
console.log(diff);
};
sym([1, 2, 3], [5, 2, 1, 4]); // [3,5,4]
I don't think your approach will work; you're supposed to create an array with elementos from both arrays, so a single .map won't do the job. Filtering through both arrays should work, although it will probably leave enough room for optimization.
my newArr returns [3, 4] instead of [3,4,5]
You are using map which will only return one value per iteration (which is why you are getting only 2 values) and in your case you are checking if the index is found or not (not the item)
You need to concatenate all the arrays and then remove those which are repeated
Concatenate
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
Create a map by number of occurrences
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
Iterate and filter through those keys whose value is 1
var output = Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
Demo
function sym(args)
{
args = [].slice.call(arguments);
var newArr = args.reduce( ( a, c ) => a.concat( c ) , []);
var map = newArr.reduce( (a,c) => ( a[c] = (a[c] || 0) + 1, a ) , {});
return Object.keys( map ).filter( s => map[s] === 1 ).map( Number );
}
console.log(sym([1, 2, 3], [5, 2, 1, 4]));
You could take an Object for counting the items and return only the items which have a count.
function sym(array) {
return array.reduce((a, b) => {
var count = {};
a.forEach(v => count[v] = (count[v] || 0) + 1);
b.forEach(v => count[v] = (count[v] || 0) - 1);
return Object.keys(count).map(Number).filter(k => count[k]);
});
}
console.log(sym([[3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]]));