I have a multidimensional array. I want to group the values in this and know how many.
I've created a new array. I've looped a multidimensional array. If the current value does not exist in the new array, I add this value into the array. But I couldn't do it dynamically, they were all added to the bottom. I couldn't add it to "subCategories".
In this way I have a multidimensional array.
currentArray = [
[1, 2, 3, 5],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]
]
I used a loop like this.
newArray= [];
for (let i = 0; i < currentArray.length; i++) {
for (let k = 0; k < currentArray[i].length; k++) {
let obj = { id: currentArray[i][k], subCategories: [] };
let index = newCategories.findIndex(x => x.id === obj.id);
if (index === -1) {
if (k === 0) {
newCategories.push(obj);
}
} else {
newCategories[index].subCategories.push(obj);
}
}
}
I used a loop like this but I did not get a successful result. Logic error in the current code and I couldn't figure it out.
I want the same elements in the array to be added to the new array only once. And I want to get "count" in the last elements.
So the output I want to achieve is as follows.
{
"id": 1,
"subCategories": [
{
"id": 2,
"subCategories": [
{
"id": 3,
"subCategories": [
{
"id": 5,
"count": 1,
"subCategories": []
},
{
"id": 4,
"count": 6,
"subCategories": []
}
]
}
]
}
]
}
You could reduce the array by reduceing the inner array and look for the wanted id.
var array = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
result = array
.reduce((r, a) => {
var o = a.reduce((p, id) => {
var temp = p.subCategories.find(q => q.id === id);
if (!temp) {
p.subCategories.push(temp = { id, subCategories: [] });
}
return temp;
}, r);
o.count = (o.count || 0) + 1;
return r;
}, { subCategories: [] })
.subCategories;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is in the same style as you had, by using a starting object which matches the inner format and a search for the items for returning this object for next level.
var currentArray = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
newArray = [],
temp,
item;
for (let i = 0; i < currentArray.length; i++) {
temp = { subCategories: newArray };
for (let k = 0; k < currentArray[i].length; k++) {
item = temp.subCategories.find(x => x.id === currentArray[i][k]);
if (!item) {
temp.subCategories.push(item = { id: currentArray[i][k], subCategories: [] });
}
temp = item;
}
temp.count = (item.count || 0) + 1;
}
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I am learning JS, and I have homework. I am asked to transform array into new array where each item is represented by the running count of element appearances.
For example
[1, 2, 1, 1, 3]
becomes
[1, 1, 2, 3, 1]
I wrote a code which works for numbers, but fails tests with strings:
UPDATE: IT works for some numbers, for others does not :/
function duplicates(arr) {
let i, j, newArr = [],
count = 1;
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr.length; j++) {
if (i == j) {
continue
}
if (arr[i] === arr[j]) {
newArr.push(count++)
break
}
}
if (j === arr.length) {
newArr.push(1)
}
}
return newArr
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
Can you give me a hint? :/
Thank you!
One approach is to use .map(), .slice() and .filter()
const duplicates = (nums) =>
nums.map((value, index) => {
const segment = nums.slice(0,index+1);
return segment.filter(v => v === value).length;
});
console.log(duplicates([1, 2, 1, 1, 3]));
console.log(duplicates([1, 2, 1, 2, 3, 1]));
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa']));
map creates a new array by iterating through nums and transforming each value via a function
slice is used to create a new array based on nums. In first example, the new array is [1] in first iteration, [1,2] in second, followed by [1,2,1] and so on.
filter finds the items in the array from #2 that match the current value.
Elaborating on #CRice and #Kaiido idea, let's create an object that is creating the count of the items while you're looping through the array:
function duplicates(arr) {
const obj = {};
let value = 0;
let newArr = [];
for (i = 0; i < arr.length; i++) {
value = arr[i];
if (obj[value]){
obj[value] = obj[value] + 1;
}
else{
obj[value] = 1;
}
newArr.push(obj[value]);
}
return newArr
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
JS has a nice built-in, reduce, that does so in a simpler way:
const duplicates = (arr) => {
const obj = {}
return arr.reduce ( (acc,cur) => {
obj[cur] = (obj[cur])?obj[cur]+1:1
acc.push(obj[cur])
return acc
}, []);
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
I have an array of items that I would like to remove from within a nested object array, if present.
var itemsToRemove = [1, 2, 3];
var data = [
{ id: 'a', list: [1, 3, 4, 5] },
{ id: 'b', list: [2, 6, 7] }
];
should update to
data = [
{ id: 'a', list: [4, 5] },
{ id: 'b', list: [6, 7] }
];
I am able to cut it down to two loops (below) instead of three, but I'm wondering if there's any way to simplify it to one loop/unnested loops.
data.forEach(obj => {
var map = {};
obj.list.forEach(el => map[el] = true);
itemsToRemove.forEach(el => if(map[el] { delete map[el] }));
obj.list = Object.keys(map);
});
You could take Array#filter with Array#includes.
const
itemsToRemove = [1, 2, 3],
data = [
{ id: 'a', list: [1, 3, 4, 5] },
{ id: 'b', list: [2, 6, 7] }
];
data.forEach(o => o.list = o.list.filter(v => !itemsToRemove.includes(v)));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you need a more faster approach, you could take a Set.
const
itemsToRemove = [1, 2, 3],
data = [
{ id: 'a', list: [1, 3, 4, 5] },
{ id: 'b', list: [2, 6, 7] }
],
remove = new Set(itemsToRemove);
data.forEach(o => o.list = o.list.filter(v => !remove.has(v)));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This question already has answers here:
How to find all subsets of a set in JavaScript? (Powerset of array)
(14 answers)
Closed 2 years ago.
Question is basically to generate all subsets given an array of unique integers.
For example powerSet[1,2,3] should return [[], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3]]
Here is my recursive attempt:
function powerset(array) {
// Write your code here.
let set = [[]];
powersetHelper(array, [], set);
return set;
}
function powersetHelper(array, subset, set) {
if (array.length === 0) return;
for (let i = 0; i < array.length; i++) {
subset.push(array[i]);
set.push(subset);
}
let newArr = array.slice(1);
powersetHelper(newArr, [], set)
}
Why is this returning [[], [1, 2, 3], [1, 2, 3], [1, 2, 3], [2, 3], [2, 3], [3]] instead of the correct solution?
Additionally, I have attempted this iteratively as follows:
function powerset(array) {
// Write your code here.
let subset = [];
let set = [[]];
while (array.length > 0) {
for (let j = 0; j < array.length; j++) {
let num = array[j];
subset.push(num);
set.push(subset);
}
array = array.slice(1);
}
return set;
}
This is also incorrect and somehow returning what I have below even though it seems to be the same logic as my recursive solution
[
[],
[1, 2, 3, 2, 3, 3],
[1, 2, 3, 2, 3, 3],
[1, 2, 3, 2, 3, 3],
[1, 2, 3, 2, 3, 3],
[1, 2, 3, 2, 3, 3],
[1, 2, 3, 2, 3, 3]
]
You need to take a copy without object reference.
function powerSet(array) {
// Write your code here.
let set = [[]];
powersetHelper(array, [], set);
return set;
}
function powersetHelper(array, subset, set) {
if (array.length === 0) return;
for (let i = 0; i < array.length; i++) {
subset.push(array[i]);
set.push([...subset]); // take copy without object reference
}
let newArr = array.slice(1);
powersetHelper(newArr, [], set)
}
console.log(powerSet([1, 2, 3])); // [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Second one, with the same problem
function powerSet(array) {
// Write your code here.
let subset = [];
let set = [[]];
while (array.length > 0) {
for (let j = 0; j < array.length; j++) {
let num = array[j];
subset.push(num);
set.push([...subset]);
}
array = array.slice(1);
}
return set;
}
console.log(powerSet([1, 2, 3])); // [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have two arrays:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
Using arr1 as the baseline, arr2 does not match because it contains three 1's whereas arr1 only has two. arr3, however should return true because it has elements from arr1.
I tried
if(_.difference(arr2, arr1).length === 0)
But this does not take into account the number of occurrences
You could count all value from the first array and iterate the second with every and return early if a value is not given or zero, otherwise decrement the count and go on.
function check(a, b) {
var count = a.reduce((o, v) => (o[v] = (o[v] || 0) + 1, o), {});
return b.every(v => count[v] && count[v]--);
}
var arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3];
console.log(check(arr1, arr2)); // false
console.log(check(arr1, arr3)); // true
You can try to loop through second array and compare t against main array if value found make main array cell to false and set flag as true
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function checkArray(compareMe, toThis){
var flag = false;
for(var i = 0; i < toThis.length; i++){
flag = false;
for(var j = 0; j < compareMe.length; j++){
if(compareMe[j] == toThis[i]){
compareMe[j] = false;
flag = true;
break;
}
}
}
return flag;
}
console.log(checkArray(arr1, arr2));
console.log(checkArray(arr1, arr3));
Try this solution:
const arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3],
arr4 = [1, 2, 3, 1, 2, 3, 4, 1];
function containsFn(src, trg) {
const srcCp = src.slice();
for(let i = 0; i < trg.length; i++) {
const index = srcCp.indexOf(trg[i]);
if(index > - 1) {
srcCp.splice(index, 1);
} else {
return false;
}
}
return true;
}
console.log(containsFn(arr1, arr2));
console.log(containsFn(arr1, arr3));
console.log(containsFn(arr1, arr4));
Looks like I'm already late to the party but this would be a recursive solution:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function findAllIn(find, search) {
if (find.length == 0) {
return true;
}
i = search.indexOf(find[0]);
if (i == -1) {
return false;
}
return findAllIn(find.slice(1,find.length), search.slice(0,i).concat(search.slice(i+1, search.length)));
}
console.log(findAllIn(arr2, arr1)); // false
console.log(findAllIn(arr3, arr1)); // true
This should do the trick
Not efficient but I think it is easy to understand
const count = (x, xs) =>
xs.reduce((y, xi) => y + (xi === x ? 1 : 0), 0)
const isInclusive = (xs, ys) =>
xs.every((xi) => count(xi, xs) >= count(xi, ys))
const arr1 = [1, 2, 3, 1, 2, 3, 4]
const arr2 = [1, 3, 1, 1]
const arr3 = [1, 1, 2, 2, 3]
console.log(isInclusive(arr1, arr2))
console.log(isInclusive(arr1, arr3))
Based on this answer: https://stackoverflow.com/a/4026828/3838031
You can do this:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
console.log((arr1.diff(arr2).length === 0) ? "True" : "False");
console.log((arr1.diff(arr3).length === 0) ? "True" : "False");
console.log((arr2.diff(arr1).length === 0) ? "True" : "False");
console.log((arr2.diff(arr3).length === 0) ? "True" : "False");
console.log((arr3.diff(arr1).length === 0) ? "True" : "False");
console.log((arr3.diff(arr2).length === 0) ? "True" : "False");
Having a matrix which contains many sub-arrays. Each array has the same length and each first element of them is a string followed by numeric elements having this form:
myArray = [
["revenues", 10, 20, 30],
["expenses", 1, 1, 1],
["expenses", 2, 3, 4],
["revenues", 5, 6, 7],
];
My goal is to combine them by string and compute the sum on each position. For the above example the result must be:
result = [
["revenues", 15, 26, 37],
["expenses", 3, 4, 5],
];
I tried to do it by mapping them by the value of the string and than compute sum for each position. It's only for the sub-arrays strings containing "revenues" in the first phase but still not working.
result = myArray.map(s => s[0].includes("revenues")).reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = (r[i] || 0) + b;
});
return r;
}, []);
Any suggestions?
You could find the sub array in the temporary result or add that array to the result set.
var array = [["revenues", 10, 20, 30], ["expenses", 1, 1, 1], ["expenses", 2, 3, 4], ["revenues", 5, 6, 7]],
result = array.reduce((r, a) => {
var sub = r.find(([key]) => key === a[0]);
if (!sub) {
return r.concat([a]);
}
a.forEach((v, i) => i && (sub[i] += v));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var myArray = [
["revenues", 10, 20, 30],
["expenses", 1, 1, 1],
["expenses", 2, 3, 4],
["revenues", 5, 6, 7],
];
var result = myArray.reduce(function(acc, curr) {
if (acc[curr[0]]) {
acc[curr[0]] = acc[curr[0]].map(function(val, index) {
if (index) {
return val + curr[index];
}
return val
})
} else {
acc[curr[0]] = curr;
}
return acc
}, {})
console.log(Object.values(result))