Related
I have an array and want to change name in object { id: 4, name: 'name4' } to 'name6'
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
},
I try in this way but it isn't working
const name = 'name4';
const result = example?.forEach((group) =>
group.items.forEach((item) =>
if (item.name === name) {
return item.name === 'name6';
}
return null;
})
);
The for...of statement is my recommendation for readability and loop optimisation.
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' },
],
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' },
],
},
];
const oldName = 'name4';
const newName = 'name6';
for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break
}
}
}
You could even go a step further and terminate the outer loop with a label if you only need to change the name in a single group.
outerLoop: for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break outerLoop;
}
}
}
Hope this helps.
You could either change the value by simply assigning a new value.
example[1].items[0].name = 'name6'
But you can also iterate through all items and search for the name you want to change. I created a function that goes through an array and loops over its nested items arrays searching for any given name (targetName) and replacing it with a new one (newName):
function changeName(array, targetName, newName) {
// Loop through the elements of array
array.forEach((element) => {
// Check each item: change the name if it matches the target
element.items.forEach((item) => {
if (item.name === targetName) item.name = newName;
});
});
}
// This function will check example array and change
// every name that has a value 'name4' into 'name6'
changeName(example, "name4", "name6");
forEach doesn't return any value.
Instead of return item.name === 'name6' you can simply set new value to item.name.
Why not like this?
const example = [{
id: '1234',
desc: 'sample1',
items: [{
id: 1,
name: 'name1'
},
{
id: 2,
name: 'testItem2'
}
]
},
{
id: '3456',
desc: 'sample2',
items: [{
id: 4,
name: 'name4'
},
{
id: 5,
name: 'testItem5'
}
]
},
]
example[1].items[0].name = 'name6'
console.log(example)
I have 2 arrays like this:
blockedUsers = ['u1', 'u2', 'u3']
videoList = [
{
id: 1,
mp4URL: '...mp4',
user: {
id: 'u1',
name: 'User 1'
}
},
{
id: 2,
mp4URL: '...mp4',
user: {
id: 'u2',
name: 'User 1'
}
},
{
id: 3,
mp4URL: '...mp4',
user: {
id: 'u5',
name: 'User 1'
}
}
]
I want to remove blocked users from video array. At final I will get array has 1 video from u5. How to do that?
Thank you
Filter out elements where user id is not included in blocked users.
videoList.filter(v => !blockedUsers.includes(v.user.id))
There are a lot of methods for the same. Please find Array.filter implementation of the same.
const blockedUsers = ['u1', 'u2', 'u3']
const videoList = [
{ id: 1, mp4URL: '...mp4', user: { id: 'u1', name: 'User 1' } },
{ id: 2, mp4URL: '...mp4', user: { id: 'u2', name: 'User 1' } },
{ id: 3, mp4URL: '...mp4', user: { id: 'u5', name: 'User 1' } }
];
const output = videoList.filter((node) => blockedUsers.indexOf(node.user.id) === -1);
console.log(output);
There are two ways
First way, you could filter the videoList and iterate to check if the user is in the blocked list. We could do this with .includes for blockedUsers, but this way will result in the complexity of O(n*m), given that n is the length of blockedUsers and m is the length of videoList
Second way, you could first turn the blockedUsers into a hash table using Set. This will reduce the query time complexity for blockedUsers from O(n) to O(1). In this way, the overall time complexity would be O(n + m), which is better than the first way
const blockedUsers = ["u1", "u2", "u3"]
const videoList = [ { id: 1, mp4URL: "...mp4", user: { id: "u1", name: "User 1", }, }, { id: 2, mp4URL: "...mp4", user: { id: "u2", name: "User 1", }, }, { id: 3, mp4URL: "...mp4", user: { id: "u5", name: "User 1", }, }, ]
const blockedUsersHashTable = new Set(blockedUsers)
const res = videoList.filter(
({ user: { id } }) => !blockedUsersHashTable.has(id)
)
console.log(res)
If time complexity is not your concern, just go with the first way.
You can filter the list by using the Array.prototype.includes() method.
const
blockedUsers = ['u1', 'u2', 'u3'],
videoList = [
{ id: 1, mp4URL: '...mp4', user: { id: 'u1', name: 'User 1' } },
{ id: 2, mp4URL: '...mp4', user: { id: 'u2', name: 'User 1' } },
{ id: 3, mp4URL: '...mp4', user: { id: 'u5', name: 'User 1' } }
],
allowedVideos = videoList.filter(({ user: { id } }) => !blockedUsers.includes(id));
console.log(allowedVideos);
.as-console-wrapper { top: 0; max-height: 100% !important; }
const blockedUsers = ['u1', 'u2', 'u3'];
const videoList = [
{
id: 1,
mp4URL: '...mp4',
user: {
id: 'u1',
name: 'User 1'
}
},
{
id: 2,
mp4URL: '...mp4',
user: {
id: 'u2',
name: 'User 1'
}
},
{
id: 3,
mp4URL: '...mp4',
user: {
id: 'u5',
name: 'User 1'
}
}
];
const target = videoList.filter(v => !blockedUsers.includes(v.user.id));
console.log(target);
I'm trying to merge data from two API in the new API but I'm getting error Uncaught TypeError: Cannot read property 'lastName' of undefined. This is happening because is not finding lastName when it missing in API2. In this case, I want to initialize it as empty string if not able to find that in the merged API.
API 1 look:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
}
}
API2 look:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3'
}
}
Merged API look:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second Test' //lastName from API 2 + name from API 1
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
lastName: '' //lastName from API 2 + name from API 1
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1 Test 2' //lastName from API 2 + name from API 1
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3 Test 3' //lastName from API 2 + name from API 1
}
Fetched data:
const [api1, setApi1] = useState([]);
const [api2, setApi2] = useState([]);
const [mergeApi, setMergeAPi] = useState([]);
useEffect(() => {
fetch('api1')
.then((response) => {
setApi1(response);
});
});
useEffect(() => {
fetch('api2')
.then((response) => {
setApi2(response);
});
});
useEffect(() => {
const data = api1.map(getData => {
const d = api2.find((object) => {
// return object.name === api1.name;
return object.name === getData.name;
});
return {
...api1,
// name: `${d.name} - ${d.lastName}`
name: d ? `${d.name} - ${d.lastName}` : ""; // solution there
}
});
});
You can use inline || operator:
${d.lastName || ""}
Also you have to change the object here:
return object.name === getData.name; //<----change here.
Try to do this. There could be a case when in your real data could be some name taht not exist in api2.
const data = api1.map(getData => {
const d = api2.find((object) => {
return object.name === getData.name; // <---- NOTE THIS
});
if (!d) { // <---- NOTE THIS
return getData
}
return {
...getData, // <---- NOTE THIS
name: `${d.name} - ${d.lastName}`
}
});
idk if you really have id in objects, but I would compare by id, not by name.
return object.id === getData.id;
useEffect(() => {
const data = api1.map(getData => {
const d = api2.find((object) => {
// return object.name === api1.name;
return object.name === getData.name; //replaced but doesn't work
});
return {
...api1,
name: `${d.name} - ${d.lastName}`
}
});
});
Array.find return undefined if there is no matched item on array. so d could be undefind and therefore code throws error there is no 'lastname' property on d.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
useEffect(() => {
const data = api1.map(getData => {
...
if (d) {
return {
...getData,
name: `${d.name} - ${d.lastName}`
}
} else {
// some code to handle if d is undefined
}
});
});
Try something like this.
1) api1 and api2 data look like object, have Object.values to get array of values.
2) Change to find function. Checking obj2.name === obj1.name
3) check for d value before using.
const api1 = {
0: {
id: 1234,
company: "String",
name: "Test"
},
1: {
id: 2345,
company: "String1",
name: "Test 1"
},
2: {
id: 3456,
company: "String2",
name: "Test 2"
},
3: {
id: 4567,
company: "String3",
name: "Test 3"
}
};
const api2 = {
0: {
id: 1234,
company: "String",
name: "Test",
lastName: "Second"
},
1: {
id: 2345,
company: "String1",
name: "Test 1"
},
2: {
id: 3456,
company: "String2",
name: "Test 2",
lastName: "Second 1"
},
3: {
id: 4567,
company: "String3",
name: "Test 3",
lastName: "Second 3"
}
};
const data = Object.values(api1).map(obj1 => {
const d = Object.values(api2).find(obj2 => obj2.name === obj1.name);
return {
...obj1,
name: d ? `${d.name} - ${d.lastName}` : obj1
};
});
console.log(data);
I have found solution for that:
useEffect(() => {
const data = api1.map(getData => {
const d = api2.find((object) => {
// return object.name === api1.name;
return object.name === getData.name;
});
return {
...api1,
// name: `${d.name} - ${d.lastName}`
name: d ? `${d.name} - ${d.lastName}` : ""; // solution there
}
});
});
I have three state, two of them have data from different api and the third state need to merge those two states based on IDs. So, the third state should have every data that state1 have and state2 don't have, and every data that state2 have and state1 don't have.
Api1:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
}
}
Api2:
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
lastName: 'Second 2'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3'
}
}
New Array should be (lastName = name + lastName :
data: {
0: {
id: 1234
company: 'String',
name: 'Test'
lastName: 'Second Test'
}
1: {
id: 2345
company: 'String1',
name: 'Test 1'
lastName: 'Second 2 Test 1'
}
2: {
id: 3456
company: 'String2',
name: 'Test 2'
lastName: 'Second 1 Test 2'
}
3: {
id: 4567
company: 'String3',
name: 'Test 3'
lastName: 'Second 3 Test 3'
}
Fetched Data:
const [state1, setState1] = useState([]);
const [state2, setState2] = useState([]);
const [mergeStates, setMergeStates] = useState([]);
useEffects(() => {
fetch("api1")
.then(data =>{
state1(data);
})
fetch("api2")
.then(data =>{
state2(data);
})
}, []);
useEffects(() => {
// Here I want to merge the responses based on IDs
const lastName = companies.map((response) => ({
name: response.name,
lastName: `${response.name} - ${response.lastName}`
}));
setMergeState(lastName);
}, [state1, state2]);
So, the api2 has lastName that api1 doesn't have. So, the mergedStates need to include that.
based on your quetion to merge the two state, you need to iterate over a state either 1 or 2, while merging them.
useEffects(() => {
const keys = Object.keys(state1);
mergedData = keys.map(key => {
// key will be 0,1,2 as with your quetion
return {
...state1[key],
...state2[key],
fullName: state1[key].name + state2[key].lastName
};
});
}, [state1, state2]);
Following on from my previous question, I'd like to change and extend the capability of what was suggested.
Here's the data I've got:
const things = [
{
id: 1,
title: 'Something',
categoryId: 1
},
{
id: 2,
title: 'Another thing',
categoryId: 1
},
{
id: 3,
title: 'Yet another thing',
categoryId: 2
},
{
id: 4,
title: 'One more thing',
categoryId: 4
},
{
id: 5,
title: 'Last thing',
categoryId: 4
}
]
const categories = [
{
id: 1,
title: 'Category 1'
},
{
id: 2,
title: 'Category 2'
},
{
id: 4,
title: 'Category 3'
}
]
Previously I've been shown how to do something along these lines:
const duplicatesCountWithTitle = (things, categories) => {
const thingsReduced = things.reduce((hash, { categoryId }) => {
hash[categoryId] = (hash[categoryId] || 0) + 1
return hash
}, {})
}
As you'd be able to tell, the problem with this is that it actually returns a new object, and not a new array. Also I'd like to join the categoryTitle from the categories array with the results of the duplicated count from the things array, based on the categoryId matching the id in categories.
// currently the above returns an object in the structure of:
// {
// 1: 2,
// 2: 1,
// 4: 2
// }
// what I'm after is an array like this:
// [
// { 'Category 1': 2 },
// { 'Category 2': 1 },
// { 'Category 3': 2 }
// ]
Thanks in advance, again.
Something like this?
const newArr = categories.map(category => {
const count = things.filter(thing => thing.categoryId === category.id).length;
return { [category.title]: count }
});
console.log(newArr);
https://jsfiddle.net/f3x6m12j/
You could take a Map for the relation of id and title.
const
duplicatesCountWithTitle = (things, categories) => things.reduce((hash, { categoryId }) => {
hash[categories.get(categoryId)] = (hash[categories.get(categoryId)] || 0) + 1
return hash;
}, {}),
things = [{ id: 1, title: 'Something', categoryId: 1 }, { id: 2, title: 'Another thing', categoryId: 1 }, { id: 3, title: 'Yet another thing', categoryId: 2 }, { id: 4, title: 'One more thing', categoryId: 4 }, { id: 5, title: 'Last thing', categoryId: 4 }],
categories = [{ id: 1, title: 'Category 1' }, { id: 2, title: 'Category 2' }, { id: 4, title: 'Category 3' }],
result = duplicatesCountWithTitle(
things,
categories.reduce((m, { id, title }) => m.set(id, title), new Map)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }