Reference id into array and map it - javascript

i have such an array.
[
{
id: 1,
title: "one",
cats: [],
},
{
id: 2,
title: "two",
cats: [{ id: 3 }, { id: 4 }],
},
{
id: 3,
title: "sub 1",
cats: [],
},
{
id: 4,
title: "sub 2",
cats: [],
},
];
How can i correctly reference the id 3 and 4 to the nested array of cats.
I need to achive the following.
I need to display the list as buttons, but the ones that have nested to be dropdown.
example
one.title
two.title
sub1.title
sub2.title
I dont want to have id 3 and 4 data like title, into the nested array because the router takes ID as param, so basically when i will click on sub1 it should display data from id-3.
Please help me understand this as i am new.
Thank you.

You can use this function to fomat your array:
const formatArray = arr =>
arr.map(item => {
if (item.cats.length === 0) {
return item;
} else {
const itemtFormatted = { ...item };
itemtFormatted.cats = item.cats.map(cat =>
arr.find(e => e.id === cat.id)
);
return itemtFormatted;
}
});
You can check here: https://jsfiddle.net/x9o6dkum/

Related

How to get the values of an array of an array of objects

So I have an array like this. Like array containing an array of objects.
posts = [
[
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
}
],
[
{
"id": 2,
"info": "This is a duplicated id I want to remove"
},
{
"id": 4,
"info": "This information is safe"
}
]
]
I want to get the elements from each array and create a new array that only has the objects at the same time removing duplicated ids.
What am trying to achieve is something like
posts = [
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
},
{
"id": 4,
"info": "This information is safe"
}
]
This is the code I have so far
id = ids.map(val => {
for(let i in val) {
console.log(val)
}
return something
})
I keep getting undefined values. I have tried forEach, for loop.
Any help would be much appreciated. Thanks
Use flat to get a flattened array of objects, and then loop over the array. If the current object's id can't be found in an object in the output array push that object.
const posts=[[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const out = [];
for (const obj of posts.flat()) {
const found = out.find(f => obj.id === f.id);
if (!found) out.push(obj);
}
console.log(out);
You could use .flat() and then .filter():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = posts.flat().filter((x, i, self) => i === self.findIndex((y) => x.id === y.id));
console.log(newPosts);
Another potential (and more optimal) solution could be this using .reduce():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = Object.values(posts.flat().reduce((acc, curr) => {
return {
...acc,
...(!acc[curr.id] ? { [curr.id]: curr } : undefined),
};
}, {}));
console.log(newPosts);
Or, if you don't like .reduce(), you can do something very similar with the Map object and a for...of loop:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const map = new Map();
for (const item of posts.flat()) {
if (map.has(item.id)) continue;
map.set(item.id, item);
}
const newPosts = Array.from(map.values());
console.log(newPosts);
Or even use a classic for loop to get the job done:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const flattened = posts.flat();
const map = {};
for (let i = 0; i < flattened.length; i++) {
if (map[flattened[i].id]) continue;
map[flattened[i].id] = flattened[i];
}
console.log(Object.values(map));
Either way, in each of these examples we're following the same workflow:
Flatten the array so that all items are on the same level.
Filter out the items with the duplicate IDs.
I group by id in order to remove duplicates.
var posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
var agg = {}
posts.forEach(function(arr) {
arr.forEach(function(item) {
agg[item.id] = agg[item.id] || item
})
})
console.log(Object.values(agg))
.as-console-wrapper {
max-height: 100% !important
}
Flatten the array with flat, then use a set to keep track of the ids we already have. The ternary inside the filter is logic to check if the id is already in the set, and if it is, we filter the item out. Otherwise, we add the id back to the set.
const posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const flat = posts.flat();
const ids = new Set();
const filtered = flat.filter((item) => ids.has(item.id) ? false : ids.add(item.id));
console.log(filtered);
.as-console-wrapper {
max-height: 100% !important
}
There are two things we need to do:
Flatten the inner areas into one main array with array.prototype.flat()
Remove duplicates based on, I'm assuming, the order of their appearance in the data.
We can do this by reducing the flattened array to an object with a condition that doesn't add any present id's if they're found
Then we convert that object to an array using Object.values()
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let flattened = posts.flat()
console.log('Flattened: ', flattened)
let unique = flattened.reduce((acc, obj) => {
if (!acc[obj.id]) {
acc[obj.id] = obj
}
return acc
}, {})
console.log('Unique Objects: ', unique)
let result = Object.values(unique)
console.log('Final Array: ' ,result)
Doing it in one go and with a spread ... object merge:
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let result = Object.values(
posts.flat().reduce((acc, obj) =>
({...{[obj.id]: obj}, ...acc})
, {})
);
console.log('Final Array: ', result);

Get cumulative length of array inside array of objects

I have been searching for quite a while but cannot find an answer to my issue. The problem is pretty simple; I have an array of objects, each containing another array of objects. I want to get the cumulative length of all arrays inside all objects.
Here is some sample data:
const items = [
{
id: 1,
title: "Test 1",
data: [
{
...
},
{
...
},
]
},
{
id: 2,
title: "Test 2",
data: [
{
...
},
]
}
]
In this sample, the length should be 3 since there is 2 objects inside the first object's data property and 1 object inside the second object's data property.
pretty simple
const items =
[ { id: 1, title: "Test 1", data: [{a:1},{a:1} ] }
, { id: 2, title: "Test 2", data: [{a:1},{a:1},{a:1},{a:1}] }
]
console.log('cumulative length ', items.reduce((a,c)=>a+c.data.length,0) )
All you need to do is loop through all items you got, check the length of the data array of each item and add that length to a variable. Here is a working snippet:
const items = [
{
id: 1,
title: "Test 1",
data: [
{
},
{
},
]
},
{
id: 2,
title: "Test 2",
data: [
{
},
]
}
];
// Initialize the count variable as 0
var count = 0;
// Pass through each item
items.forEach((item)=>{
// Adding the count of data of each item
count += item.data.length;
});
// Outputting the count
console.log(count);
If you want to use for of loop that works too.
const countObjects = (arrayInput) => {
let totalCount = 0
for(let item of items) {
totalCount += item.data.length
}
return totalCount
}
console.log(countObjects(items))

How to add a value to an array in a specific location with JS

I have an array of objects:
const array = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 }
];
and I need to add another entry to it, but it needs to be placeable within any location in the array. So for example:
array.push({ id: 5, after_id: 2 }); and this should place the new entry between ids 2 and 3. Is there some standard way of doing this?
#p.s.w.g Has posted what is probably the best solution in a comment already, but I thought I'd post my original solution here as an answer now this is reopened.
You can use some to iterate through the array until the correct index is found, then you can slice the array and insert the item at the relevant index:
const arrayTest = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
const insertAfterId = (array, item, idAfter) => {
let index = 0;
array.some((item, i) => {
index = i + 1;
return item.id === idAfter
})
return [
...array.slice(0, index),
item,
...array.slice(index, array.length),
];
};
const result = insertAfterId(arrayTest, {
id: 6
}, 2)
console.dir(result)

Is there any lodash method to update an object inside an array, where the item matched is going to have a new key?

Im adding a checkbox options and only have to update my object with a new key
so if I uncheck a item in a list i want to update the object
[ { id: 1 }, { id: 2 } ]
after unchecked:
[ { id: 1 }, { id: 2, unChecked: false } ]
any method to toggle this states?
thanks in advance
You can do it using native javascript and array map method. In the function check if the id matches then add the key there
let obj = [{
id: 1
}, {
id: 2
}]
function updateObj(obj, objId, key) {
return obj.map((item) => {
if (item.id === objId) {
return {
id: item.id,
[key]: false
}
} else {
return {
id: item.id
}
}
})
}
console.log(updateObj(obj, 2, 'unchecked'))
You can do this without lodash and using the .map method by adding the unChecked property if the id is not in the checked array by using .includes().
See working example below:
const checked = [2, 3], /* array holding all checked values */
arr = [{id: 1}, {id: 2}],
res = arr.map(({id}) => checked.includes(id) ? {id, unChecked: false} : {id});
console.log(res);

ImmutableJS - pushing into a nested array

const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{ id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.updateIn([0, 'subList'], function (items) {
return items.concat(newItem)
});
https://jsbin.com/midimupire/edit?html,js,console
Results in:
Error: invalid keyPath
I think that perhaps I need to set subList as a List(); I get the same error when trying this.
If I understand the question correctly, you want to return collection with the first element as:
{
id : 1,
subList: [
{name: "sublist item"}
]
}
To do this we'll need to make a few changes.
Use Immutable.fromJS to deeply convert the plain JS array of objects to an Immutable List of Maps
Use List.update() to return a new List with the updated value
Use Map.updateIn() to return a new LMapist with the updated value
Here's the whole thing:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.update(0, item => {
return item.updateIn(['subList'], subList => {
return subList.concat(newItem);
});
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
And the result:
[
{
"id": 1,
"subList": [
{
"name": "sublist item"
}
]
},
{
"id": 2,
"subList": []
},
{
"id": 3,
"subList": []
}
]
Update: List.updateIn() can use an index as the keypath, so you can simplify this to the following:
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
Like this:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
Use the object you got, update the subArray and return the whole object.
const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.update([0], function (obj) {
obj.subList = obj.subList.concat(newItem)
return obj;
});
This doesn’t work because the elements of your Immutable.List are plain-old JavaScript objects (POJO), not Immutable.Maps, so updateIn doesn’t know how to work with them. You can either:
Make the objects Immutable.Maps by using Immutable.fromJS instead of Immutable.List as the constructor to convert the entire object graph to Immutable objects. (See JS Bin)
Use update([0]) instead of updateIn to just get the POJO and mutate it (as in #Navjot’s answer).

Categories

Resources