I would like to break a array with users:
[
{name: "Carlos"},
{name: "Marcos"},
{name: "Fernando"},
{name: "Jhon"},
{name: "Loius"},
{name: "Jacob"},
]
And get something like this:
[
[
{name: "Jhon"},
{name: "Loius"},
{name: "Jacob"},
],
[
{name: "Carlos"},
{name: "Marcos"},
{name: "Fernando"},
]
]
The criterion for splitting them is that I want that each sub array to have a maximum of 3 users, but the number of sub arrays can be unlimited.
function splitIntoParts(input, maxElementsPerPart) {
const inputClone = [...input]; // create a copy because splice modifies the original array reference.
const result = [];
const parts = Math.ceil(input.length/maxElementsPerPart);
for(let i = 0; i < parts; i++) {
result.push(inputClone.splice(0, maxElementsPerPart));
}
return result;
}
console.log(splitIntoParts([
{name: "Carlos"},
{name: "Marcos"},
{name: "Fernando"},
{name: "Jhon"},
{name: "Loius"},
{name: "Jacob"},
{name: "Simon"},
], 3));
chunk is elegantly expressed using functional style
const chunk = (xs = [], n = 1) =>
xs.length <= n
? [ xs ]
: [ xs.slice (0, n) ] .concat (chunk (xs.slice (n), n))
const data =
[ 1, 2, 3, 4, 5, 6 ]
console.log (chunk (data, 1))
// [ [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ], [ 6 ] ]
console.log (chunk (data, 2))
// [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
console.log (chunk (data, 3))
// [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
console.log (chunk (data, 4))
// [ [ 1, 2, 3, 4 ], [ 5, 6 ] ]
console.log (chunk ())
// [ [ ] ]
I think take and drop abstractions make the function read a littler nicer. Your opinion may vary.
const take = (xs = [], n = 1) =>
xs.slice (0, n)
const drop = (xs = [], n = 1) =>
xs.slice (n)
const chunk = (xs = [], n = 1) =>
xs.length <= n
? [ xs ]
: [ take (xs, n) ] .concat (chunk (drop (xs, n), n))
let data = [
{name: "Carlos"},
{name: "Marcos"},
{name: "Fernando"},
{name: "Jhon"},
{name: "Loius"},
{name: "Jacob"},
]
function toGroupsOf(n, array){
return Array.from(
{length: Math.ceil(array.length/n)}, //how many groups
(_,i) => array.slice(i*n, (i+1)*n) //get the items for this group
)
}
console.log(toGroupsOf(3, data));
.as-console-wrapper{top:0;max-height:100%!important}
or
function toGroupsOf(n, array){
var groups = Array(Math.ceil(array.length/n));
for(var i=0; i<groups.length; ++i)
groups[i] = array.slice(i*n, (i+1)*n);
return groups;
}
Related
My code looks like this
var res = [];
var temp = [];
function Permutations(target, size) {
if (size === 0) {
res.push(temp);
console.log(res);
return;
}
for (let i = 0; i < target.length; i++) {
if (target[i] !== null) {
temp.push(target[i]);
target[i] = null;
Permutations(target, size - 1);
target[i] = temp.pop();
}
}
}
Permutations([1, 2, 3], 2);
console.log(res);
When I run my code, I can see my res stores each permutation as it is is being executed. However, when I log it outside the function, all the stored value disappeared.
[ [ 1, 2 ] ]
[ [ 1, 3 ], [ 1, 3 ] ]
[ [ 2, 1 ], [ 2, 1 ], [ 2, 1 ] ]
[ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ] ]
[ [ 3, 1 ], [ 3, 1 ], [ 3, 1 ], [ 3, 1 ], [ 3, 1 ] ]
[ [ 3, 2 ], [ 3, 2 ], [ 3, 2 ], [ 3, 2 ], [ 3, 2 ], [ 3, 2 ] ]
[ [], [], [], [], [], [] ] // This is my console.log outside the function
The array temp holds is the same array throughout the complete execution of your code. And res.push(temp); adds this same array (not a copy of it) to your res array.
Here a related question about how Objects are handled in JavaScript: Is JavaScript a pass-by-reference or pass-by-value language?
So your code results in res having N times the same array.
You could copy the element stored in temp to a new array using [...temp], and push that to your res array.
var res = [];
var temp = [];
function Permutations(target, size) {
if (size === 0) {
res.push([...temp]);
return;
}
for (let i = 0; i < target.length; i++) {
if (target[i] !== null) {
temp.push(target[i]);
target[i] = null;
Permutations(target, size - 1);
target[i] = temp.pop();
}
}
}
Permutations([1, 2, 3], 2);
console.log(res);
I have similiary problem like here: JavaScript: Find all parents for element in tree recursive
But I don't find path by name but by direct path.
const path = ["name1", "name4", "name5"];
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
];
It returns every possible path or nothing.
When path is too short, it returns nothing.
When path is too long, it returns nothing.
Thanks for help!
Examples of desired output:
const path = ["name1", "name4", "name5"];
findAPath(data, path)
Returns: ["name1", "name4", "name5"]
const path = ["name1", "name7", "name5"];
findAPath(data, path)
Returns []
const path = ["name1", "name4", "name5", "name5"];
findAPath(data, path)
Returns []
My trying:
let index = 0;
function find(data, index) {
let index = index;
data.some((o) => {
if(o.name == path[index]) {
index++;
find(o.tree, index);
}
});
// I don't know what return here.
// I need to probably return path where I am.
return <>;
}
using Array.prototype.flatMap
Here's a functional solution using a mutual recursion technique -
const None =
Symbol ()
const findPath = (tree = [], names = [], r = []) =>
tree.length && names.length // base: and
? tree.flatMap(branch => findPath1(branch, names, r))
: tree.length || names.length // inductive: xor
? []
: [ r ] // inductive: nor // inductive: nor
const findPath1 = ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = []) =>
name === "" && q === None // base: and
? [ r ]
: name === "" || q === None || name !== q // inductive: xor
? []
: findPath(tree, more, [ ...r, q ]) // inductive: nor
findPath(data, ["name1", "name4", "name5"])
// => [ [ "name1", "name4", "name5" ] ]
NB if your data contains multiple paths to the input values, all paths will be returned -
const data = [
{
'name': 'name1', // name1
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4', // name1->name4
'tree': [
{'name': 'name5'}, // name1->name4->name5
{'name': 'name6'}
]
},
{
'name': 'name4', // name1->name4
'tree': [
{'name': 'name5'}, // name1->name4->name5
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
Just like you asked, it returns every possible path, or nothing -
findPath(data, ["name1", "name4", "name5"])
// => [ [ "name1", "name4", "name5" ],
// [ "name1", "name4", "name5" ] ]
findPath(data, [ "name1", "name7" ])
// => [ [ "name1", "name7" ] ]
findPath(data, [ "name1", "name9" ])
// => []
When a path is too short or too long, it will return nothing -
findPath(data, [ "name1", "name4" ])
// => []
findPath(data, [ "name1", "name4", "name5", "name6" ])
// => []
Expand the snippet below to verify the results in your own browser -
const None =
Symbol ()
const findPath = (tree = [], names = [], r = []) =>
tree.length && names.length
? tree.flatMap(branch => findPath1(branch, names, r))
: tree.length || names.length
? []
: [ r ]
const findPath1 = ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = []) =>
name === "" && q === None
? [ r ]
: name === "" || q === None || name !== q
? []
: findPath(tree, more, [ ...r, q ])
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
console.log(findPath(data, ["name1", "name4", "name5"]))
// [ [ "name1", "name4", "name5" ] ]
console.log(findPath(data, [ "name1", "name7" ]))
// [ [ "name1", "name7" ] ]
console.log(findPath(data, [ "name1", "name9" ]))
// []
using Generators
Here's an alternative implementation using generators -
const None =
Symbol ()
const findPath = function* (tree = [], names = [], r = [])
{ if (tree.length && names.length) // base: and
for (const branch of tree)
yield* findPath1(branch, names, r)
else if (tree.length || names.length) // inductive: xor
return
else // inductive: nor
yield r
}
const findPath1 = function* ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = [])
{ if (name === "" && q === None) // base: and
yield r
else if (name === "" || q === None || name !== q) // inductive: xor
return
else // inductive: nor
yield* findPath(tree, more, [ ...r, q ])
}
It has the exact same output as above, only to coerce the iterable generator into an array, we use Array.from -
Array.from(findPath(data, ["name1", "name4", "name5"]))
// => [ [ "name1", "name4", "name5" ] ]
Array.from(findPath(data, [ "name1", "name7" ]))
// => [ [ "name1", "name7" ] ]
Array.from(findPath(data, [ "name1", "name9" ]))
// => []
Expand the snippet below to verify the results in your own browser -
const None =
Symbol ()
const findPath = function* (tree = [], names = [], r = [])
{ if (tree.length && names.length)
for (const branch of tree)
yield* findPath1(branch, names, r)
else if (tree.length || names.length)
return
else
yield r
}
const findPath1 = function* ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = [])
{ if (name === "" && q === None)
yield r
else if (name === "" || q === None || name !== q)
return
else
yield* findPath(tree, more, [ ...r, q ])
}
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
console.log(Array.from(findPath(data, ["name1", "name4", "name5"])))
// [ [ "name1", "name4", "name5" ] ]
console.log(Array.from(findPath(data, [ "name1", "name7" ])))
// [ [ "name1", "name7" ] ]
console.log(Array.from(findPath(data, [ "name1", "name9" ])))
// []
how they're the same; how they're not
Note the similarity between the two implementations and how the result is formed. Both use mutual recursion. The functional solution uses expressions whereas the generator solution uses statements. The generator implementation extends a distinct advantage where by we can chose to stop or continue iteration ("finding") whenever we want.
For example, imagine an input where there are ten (10) unique paths for the given input. Perhaps we want to just return the first match,
const findFirst = (tree = [], names = []) =>
{ for (const path of findPath(tree, names))
return path
}
Or get the first three (3) matches -
const findFirst3 = (tree = [], names = []) =>
{ const r = []
for (const path of findPath(tree, names))
if (r.length < 3)
r.push(path)
return r
}
Or get the first N -
const findFirstN = (tree = [], names = [], n = 0) =>
{ const r = []
for (const path of findPath(tree, names))
if (r.length < n)
r.push(path)
return r
}
Generators are flexible like this. By contrast, the flatMap implementation is eager and always returns all results.
Having input like the below:
[
{
gameId: id_0,
groups: [1]
},
{
gameId: id_1,
groups: [2]
},
{
gameId: id_2,
groups: [1, 2]
},
{
gameId: id_3,
groups: [3]
}
]
I would like my reduce to result in an array of objects like:
[
{
group: 1,
data: [
id_0, id_2 // gameId
]
},
{
group: 2,
data: [
id_1, id_2
]
},
{
group: 3,
data: [
id_3
]
}
]
I was able to partially solve this by utilising array indexes.
The code I have currently is:
groupByArr = parameter => data => data.reduce((acc, curr) => {
curr[parameter].forEach(key => {
if (acc[key]) {
acc[key].push(curr)
} else {
acc[key] = [curr]
}
})
return acc
}, [])
which produces an array of arrays where main array index is the group id:
[
empty,
1: [
id_0, id_2
],
2: [
id_1, id_2
],
3: [
id_3
]
]
You can use Array.prototype.reduce() combined with Array.prototype.forEach() and Array.prototype.push() to return an Object and finally get the values with Object.values()
Code:
const data = [{gameId: 'id_0',groups: [1]},{gameId: 'id_1',groups: [2]},{gameId: 'id_2',groups: [1, 2]},{gameId: 'id_3',groups: [3]}]
const result = Object.values(data.reduce((acc, {gameId, groups}) => {
groups.forEach(group => {
acc[group] = acc[group] || { group, data: [] }
acc[group].data.push(gameId)
})
return acc
}, {}))
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let arr be the variable containing data in following format:
[
empty,
1: [
id_0, id_2
],
2: [
id_1, id_2
],
3: [
id_3
]
]
Then following code might do:-
for(let i=1;i<arr.length;i++){
arr[i-1] = {group:i,data:arr[i]};
}
arr[arr.length-1] = undefined; // To Prevent Unnecessary Errors
delete arr[arr.length-1]; // To Prevent Unnecessary Errors
arr.length -= -1;
Now arr will be in following format:-
[
{
group: 1,
data: [
id_0, id_2
]
},
{
group: 2,
data: [
id_1, id_2
]
},
{
group: 3,
data: [
id_3
]
}
]
Hope it helps. Tell me if I Misunderstood your question.
Use an object as acc, then add objects to it:
if (acc[key]) {
acc[key].data.push(curr)
} else {
acc[key] = { group: key, data: [curr] };
}
And finally, turn the returned object into an array:
const result = Object.values(hash);
Try this
const data = [
{gameId: 'id_0',groups: [1]},
{gameId: 'id_1',groups: [2]},
{gameId: 'id_2',groups: [1, 2]},
{gameId: 'id_3',groups: [3]}
]
const single_id = data.filter(i => i.groups.length === 1)
const multiple_ids = data.filter(i => i.groups.length > 1)
let res = [];
single_id.map(i => {
let data = {group: i.groups[0], data: [i.gameId]}
multiple_ids.forEach(o => o.groups.includes(i.groups[0]) && (data.data.push(o.gameId)))
res.push(data)
})
console.log(res)
`let b = [
{
gameId: "id_0",
groups: [1]
},
{
gameId: "id_1",
groups: [2]
},
{
gameId: "id_2",
groups: [1, 2]
},
{
gameId: "id_3",
groups: [3]
}
];
let d = new Map(); ;
b.forEach(function(a){
a.groups.forEach(function(a1){
if(d.get(a1)){
d.get(a1).push(a.gameId);
}else{
d.set(a1, [a.gameId]);
}
});
});`
You should try this one. Using reduce in grouped scenarios is the best thing JavaScript provides us
let arr = [
{
gameId: "id_0",
groups: [1]
},
{
gameId: "id_1",
groups: [2]
},
{
gameId: "id_2",
groups: [1, 2]
},
{
gameId: "id_3",
groups: [3]
}
];
const grouped = arr.reduce((acc, current) => {
for(let x of current.groups){
if(!acc[x]) {
acc[x] = {group: x, data:[current.gameId]}
} else {
acc[x].data.push(current.gameId)
}
}
return acc
},{});
console.log(Object.values(grouped))
Im new in JS and I hope you help me) I need to transform array of objects in this way:
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
...
]
const transformedArr = [
{key1: 1},
{key2: 1},
{key2: 2},
{key2: 3},
{key2: 4},
....
]
How can I do it?
Since you are new to JS ..
Good Old JS with for loops might be easy for you to understand
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
const transformedArr =[]
for(var i = 0 ; i < (arr.length); i++){
var valArr = arr[i].value
if( Array.isArray(valArr) ){ // to check if the value part is an array
for(var j=0 ; j < valArr.length ; j++){
transformedArr.push({id: arr[i].id,value:valArr[j] })
}
}else{
transformedArr.push({id: arr[i].id,value:valArr })
}
}
console.log(transformedArr)
You can use ES6 spread syntax ... with map() method.
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
var result = [].concat(...arr.map(({id, value}) => {
return Array.isArray(value) ? value.map(e => ({[id]: e})) : {[id]: value}
}))
console.log(result)
You can also use reduce() instead of map() method.
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
var result = arr.reduce(function(r, {id, value}) {
r = r.concat(Array.isArray(value) ? value.map(e => ({[id]: e})) : {[id]: value})
return r;
}, [])
console.log(result)
This proposal features Array.concat, because it add items without array and arrays to an array.
const
array = [{ id: 'key1', value: 1 }, { id: 'key2', value: [1, 2, 3, 4] }],
result = array.reduce(
(r, a) => r.concat(
[].concat(a.value).map(
v => ({ [a.id]: v })
)
),
[]
);
console.log(result);
I am trying to write a function to flatten an array. I have part of the function working and I need help in the other half.
flatten: function(anyArray, singleLevel) {
if (singleLevel == true) {
flatArray = Array.prototype.concat.apply([], anyArray);
return flatArray;
}
flatArray = Array.prototype.concat.apply([], anyArray);
if (flatArray.length != anyArray.length) {
flatArray = someObject.array.flatten(flatArray);
}
return flatArray;
}
if I type
.flatten([[[1],[1,2,3,[4,5],4],[2,3]]], true);
I want it to flatten only one level:
[[1],[1,2,3,[4,5],4],[2,3]]
Modern JavaScript allows us to handle this very easily using a variety of techniques
Using Array.prototype.flat -
const arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
const flatArr =
arr.flat(1) // 1 is default depth
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
Using Array.prototype.flatMap -
const arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
const flatArr =
arr.flatMap(x => x)
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
Using a spread argument to Array.prototype.concat
const arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
const flatArr =
[].concat(...arr)
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
Older version of JavaScript (ECMAScript 5 and below) can use techniques like Function.prototype.apply -
var arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
var flatArr =
Array.prototype.concat.apply([], arr)
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
Using Array.prototype.reduce -
var arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
var flatArr =
arr.reduce((r, a) => r.concat(a), [])
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
Using a primitive for loop -
var arr =
[ [ 1 ], [ 2, 3, [ 4, 5, [ 6 ] ] ], [ 7, [ 8, 9 ] ] ]
var flatArr =
[]
for (var i = 0; i < arr.length; i = i + 1)
flatArr = flatArr.concat(arr[i])
console.log(JSON.stringify(arr))
console.log(JSON.stringify(flatArr))
// [[1],[2,3,[4,5,[6]]],[7,[8,9]]]
// [1,2,3,[4,5,[6]],7,[8,9]]
The concat array method expects one or more arrays as arguments, whose elements will be appended:
[1].concat([2, 3], [4]) // [1, 2, 3, 4]
So if you are using apply, that will flatten another level:
[].concat.apply([1], [[2], [3]]) // === [1].concat([2], [3])
So you can either use push instead of concat, or call (or just direct invocation) instead of apply to get only a single flattening level.
if you use ES6/ES2015 you can use spread operator. Something like this
console.log(...[[[1],[1,2,3,[4,5],4],[2,3]]])