I have this sample data
let data = [{
id: 1,
info: [
'Optical',
'Drive'
]
},
{
id: 2,
info: [
'Paper',
'Binder'
]
}
];
I want to convert the array in data.info to object and assign 2 key/value pairs, first key is name with value from data.info and 2nd key is the length of data.info string. The result would be something like this:
let data = [{
id: 1,
info: [{
name: 'Optical',
length: 7
},
{
name: 'Drive',
length: 5
},
]
},
{
id: 2,
info: [{
name: 'Paper',
length: 5
},
{
name: 'Binder',
length: 6
}
]
}
];
Here's what I've tried so far
const getLength = (str) => (str.length)
const addLength = (str) => {
try {
let newObj = {};
newObj.name = str;
newObj.length = getLength(str);
return newObj;
} catch (error) {
console.log(error)
}
}
const mapedData = data.map((object) => {
object.info.map((str) => (addLength(str)))
})
However the result is
[ undefined, undefined ]
Any idea how to fix it?
To do what you require you can loop through the array, using map() to update the info array within each element. The part your original logic is missing is that you don't set the response of addLength() to the property of the parent object - you just execute the function.
let data = [{id: 1,info: ['Optical','Drive']}, {id: 2,info: ['Paper','Binder']}];
data.forEach(x => {
x.info = x.info.map(y => ({
name: y,
length: y.length
}))
});
console.log(data);
It is as simple as
data.forEach(item => item.info = item.info.map((value) => ({name: value, length: value.length})));
This should do it.
const result = data.map(curr => {
// Calculate new info array
const info = curr.info.map(item => ({name: item, length: item.length}))
// Create new object from old object and use the new info
// to not mutate the original object
return {...curr, info}
}, [])
const data = [{
id: 1,
info: [
'Optical',
'Drive'
]
},
{
id: 2,
info: [
'Paper',
'Binder'
]
}
];
console.log(data.map(e => ({
id: e.id,
info: e.info.map(i => ({
name: i,
length: i.length
}))
})));
Related
For example I have an array of objects and an array as such:
const arrayObj = [
{
id: 1,
name: "user1",
},
{
id: 2,
name: "user2",
},
{
id: 3,
name: "user3",
},
]
const array = ["user1", "user2"]
How is it I'm able to separate arrayObj into two arrays based on array as such:
const array1 = [
{
id: 1,
name: "user1",
},
{
id: 2,
name: "user2",
},
]
const array2 = [
{
id: 3,
name: "user3",
},
]
I was thinking maybe something like this:
const filteredArray = arrayObj.filter((el) => {
return array.some((f) => {
return f === el.name;
});
});
But is there a more efficient / quicker way?
Unless the arrays you're dealing with are huge, your current code is fine.
If the arrays are huge and the current code is too slow, put the names into a Set and check Set.has instead of Array.some - Set.has is much faster when there are many elements in the Set.
const userSet = new Set(array);
const usersInUserSet = arrayObj.filter(user => userSet.has(user.name));
const arr1 = [{id:'1',name:'A'},{id:'2',name:'B'},{id:'3',name:'C'},{id:'4',name:'D'}];
const arr2 = [{id:'1',name:'A',state:'healthy'},{id:'3',name:'C',state:'healthy'}];
const filterByReference = (arr1, arr2) => {
let res = [];
res = arr1.filter(el => {
return !arr2.find(element => {
return element.id === el.id;
});
});
return res;
}
console.log(filterByReference(arr1, arr2));
I've tried modifying some of the similar solutions on here but I keep getting stuck, I believe I have part of this figured out however, the main caveat is that:
Some of the objects have extra keys, which renders my object comparison logic useless.
I am trying to compare two arrays of objects. One array is the original array, and the other array contains the items I want deleted from the original array. However there's one extra issue in that the second array contains extra keys, so my comparison logic doesn't work.
An example would make this easier, let's say I have the following two arrays:
const originalArray = [{id: 1, name: "darnell"}, {id: 2, name: "funboi"},
{id: 3, name: "jackson5"}, {id: 4, name: "zelensky"}];
const itemsToBeRemoved = [{id: 2, name: "funboi", extraProperty: "something"},
{id: 4, name: "zelensky", extraProperty: "somethingelse"}];
after running the logic, my final output should be this array:
[{id: 1, name: "darnell"}, {id: 3, name: "jackson5"}]
And here's the current code / logic that I have, which compares but doesn't handle the extra keys. How should I handle this? Thank you in advance.
const prepareArray = (arr) => {
return arr.map((el) => {
if (typeof el === "object" && el !== null) {
return JSON.stringify(el);
} else {
return el;
}
});
};
const convertJSON = (arr) => {
return arr.map((el) => {
return JSON.parse(el);
});
};
const compareArrays = (arr1, arr2) => {
const currentArray = [...prepareArray(arr1)];
const deletedItems = [...prepareArray(arr2)];
const compared = currentArray.filter((el) => deletedItems.indexOf(el) === -1);
return convertJSON(compared);
};
How about using filter and some? You can extend the filter condition on select properties using &&.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
console.log(
originalArray.filter(item => !itemsToBeRemoved.some(itemToBeRemoved => itemToBeRemoved.id === item.id))
)
Or you can generalise it as well.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
function filterIfSubset(originalArray, itemsToBeRemoved) {
const filteredArray = [];
for (let i = 0; i < originalArray.length; i++) {
let isSubset = false;
for (let j = 0; j < itemsToBeRemoved.length; j++) {
// check if whole object is a subset of the object in itemsToBeRemoved
if (Object.keys(originalArray[i]).every(key => originalArray[i][key] === itemsToBeRemoved[j][key])) {
isSubset = true;
}
}
if (!isSubset) {
filteredArray.push(originalArray[i]);
}
}
return filteredArray;
}
console.log(filterIfSubset(originalArray, itemsToBeRemoved));
Another simpler variation of the second approach:
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
const removeSubsetObjectsIfExists = (originalArray, itemsToBeRemoved) => {
return originalArray.filter(item => {
const isSubset = itemsToBeRemoved.some(itemToBeRemoved => {
return Object.keys(item).every(key => {
return item[key] === itemToBeRemoved[key];
});
});
return !isSubset;
});
}
console.log(removeSubsetObjectsIfExists(originalArray, itemsToBeRemoved));
The example below is a reusable function, the third parameter is the key to which you compare values from both arrays.
Details are commented in example
const arr=[{id:1,name:"darnell"},{id:2,name:"funboi"},{id:3,name:"jackson5"},{id:4,name:"zelensky"}],del=[{id:2,name:"funboi",extraProperty:"something"},{id:4,name:"zelensky",extraProperty:"somethingelse"}];
/** Compare arrayA vs. delArray by a given key's value.
--- ex. key = 'id'
**/
function deleteByKey(arrayA, delArray, key) {
/* Get an array of only the values of the given key from delArray
--- ex. delList = [1, 2, 3, 4]
*/
const delList = delArray.map(obj => obj[key]);
/* On every object of arrayA compare delList values vs
current object's key's value
--- ex. current obj[id] = 2
--- [1, 2, 3, 4].includes(obj[id])
Any match returns an empty array and non-matches are returned
in it's own array.
--- ex. ? [] : [obj]
The final return is a flattened array of the non-matching objects
*/
return arrayA.flatMap(obj => delList.includes(obj[key]) ? [] : [obj]);
};
console.log(deleteByKey(arr, del, 'id'));
let ff = [{ id: 1, name: 'darnell' }, { id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' }]
let cc = [{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' }]
let ar = []
let out = []
const result = ff.filter(function(i){
ar.push(i.id)
cc.forEach(function(k){
out.push(k.id)
})
if(!out.includes(i.id)){
// console.log(i.id, i)
return i
}
})
console.log(result)
I have array of array of object as follows:
[
[
{
id: 1,
itemName: 'xxx',
...
},
{
id: 1,
itemName: 'yyy',
...
},
...
],
[
{
id: 2,
itemName: 'aaa',
...
},
{
id: 2,
itemName: 'kkk',
...
},
...
],
[
{
id: 3,
itemName: 'kkk',
...
},
{
id: 3,
itemName: 'yyy',
...
},
...
]
]
I am trying to check if any itemName from objects inside arrays equals given string, but I stuck at the solution that keeps these arrays with such object in one array. Here is my solution:
function isNameAcrossData(givenString){
return arr.map(arrItem =>
arrItem.find(item => item.itemId === givenString)
);
}
My solution doesn't return boolean but just one array with objects, that contain givenString and undefined as last array element. How to modify it to return just true/false value?
Use a .some inside a .some, to see if some of the arrays have at least one element inside matching the condition:
const isNameAcrossData = givenString => arr.some(
subarr => subarr.some(
({ itemName }) => itemName === givenString
)
);
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
console.log(isNameAcrossData('xxx'));
console.log(isNameAcrossData('doesntexist'));
You could also flatten the outer array first:
const isNameAcrossData = givenString => arr.flat().some(
({ itemName }) => itemName === givenString
);
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
console.log(isNameAcrossData('xxx'));
console.log(isNameAcrossData('doesntexist'));
You could check with some and return an array of boolean with using the wanted property.
function mapHasValue(key, value) {
return data.map(array => array.some(item => item[key] === value));
}
var data = [[{ id: 1, itemName: 'xxx' }, { id: 1, itemName: 'yyy' }], [{ id: 2, itemName: 'aaa' }, { id: 2, itemName: 'kkk' }], [{ id: 3, itemName: 'kkk' }, { id: 3, itemName: 'yyy' }]];
console.log(mapHasValue('id', 3));
Your code returns
[undefined, undefined, undefined]
because map returns an array so this approach won't work
You have first to loop through all the data and check inside then outside the loop assign to some variable true if there is a match.
Basically you have to return after you loop the data.
Working example for both cases:
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
function isNameAcrossData(givenString){
let isMatch = false;
arr.map(childArr => {
childArr.map(obj => obj.itemName === givenString ? isMatch = true : null);
});
return isMatch;
}
console.log(isNameAcrossData('kkk'));
console.log(isNameAcrossData('bbb'));
I had a variable like that
const data = {
code: 1,
items: [
{ nickname: 1, name: [
{id : "A"},
{id : "B"}
]
},
{
nickname: 2, name: [
{id: "A"},
{id: "C"}
]
}
]
}
after that, I want to show how many characters: A:2, B:1, C:1
You can do that is following steps:
Use flatMap() on the array data.items
Inside flatMap() use map() to convert all the object to their id and return it from flatMap(). This way you will array ["A","B","A","C"]
Then use reduce() and get an object with count of all the letters.
const data = { code: 1, items: [ { nickname: 1, name: [ {id : "A"}, {id : "B"} ] }, { nickname: 2, name: [ {id: "A"}, {id: "C"} ] } ] }
const res = data.items.flatMap(x =>
x.name.map(a => a.id)
).reduce((ac,a) => (ac[a] = ac[a] + 1 || 1,ac),{});
console.log(res)
const data = {
code: 1,
items: [
{
nickname: 1,
name: [
{ id: "A" },
{ id: "B" }
]
},
{
nickname: 2,
name: [
{ id: "A" },
{ id: "C" }
]
}
]
};
const res = data.items.reduce((acc, next) => {
next.name.forEach(({ id }) => {
acc[id] = acc[id] + 1 || 1;
});
return acc;
}, {});
console.log(res);
You can do that in a single shot using reduce.
Reducing data.items will allow you to add to the accumulator (initially an empty object), the value of the currently looped name property item.
The result will be an object owning all the occurences of each encountered letter in the name property of each array.
Relevant lines explained:
data.items.reduce((acc, next) will call the reduce method on data.items. acc is the reduce accumulator (initially an empty object), next is the currently looped item of data.items.
next.name.forEach(({id}) in this line, we loop the name property of the currently looped item (data.items[n]). ({id}) is a short syntax to acquire the id property of the looped item in the foreach. It's equivalent to (item => item.id).
acc[id] = acc[id] + 1 || 1; tries to increase the property [id] of the accumulator (example: "A" of {}) by 1. If it does not exist, it sets the value to 1.
return acc; returns the accumulator.
You could iterate name and take id in a loop for assigning the count.
const
data = { code: 1, items: [{ nickname: 1, name: [{ id : "A" }, { id : "B" }] }, { nickname: 2, name: [{ id: "A" }, { id: "C" }] }] },
result = data.items.reduce(
(r, { name }) => (name.forEach(({ id }) => r[id] = (r[id] || 0 ) + 1), r),
{}
);
console.log(result);
My code has an array of elements as follows:
element: { fromX: { id: ... } , toX: { id: ... } }
Requirement is to pull all the fromX ids into one array, and all toX ids into other.
There are a couple of different ways,
such as using foreach, reduce, iterating for each respectively, but I'm searching for an optimal functional way to return two arrays with one mapping?
Using Array#reduce and destructuring
const data=[{fromX:{id:1},toX:{id:2}},{fromX:{id:3},toX:{id:4}},{fromX:{id:5},toX:{id:6}},{fromX:{id:7},toX:{id:8}}]
const [fromX,toX] = data.reduce(([a,b], {fromX,toX})=>{
a.push(fromX.id);
b.push(toX.id);
return [a,b];
}, [[],[]]);
console.log(fromX);
console.log(toX);
You could take an array for the wanted keys and map the value. Later take a destructuring assignment for getting single id.
const
transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []),
array = [{ fromX: { id: 1 }, toX: { id: 2 } }, { fromX: { id: 3 }, toX: { id: 4 } }],
keys = ['fromX', 'toX'],
[fromX, toX] = transpose(array.map(o => keys.map(k => o[k].id)));
console.log(fromX);
console.log(toX);
Try this:
const arr = [{
fromX: {
id: 1
},
toX: {
id: 2
}
}, {
fromX: {
id: 3
},
toX: {
id: 4
}
}]
let {
arr1,
arr2
} = arr.reduce((acc, {
fromX,
toX
}) => ({ ...acc,
arr1: [...acc.arr1, fromX],
arr2: [...acc.arr2, toX]
}), {
arr1: [],
arr2: []
})
console.log(arr1, arr2);
You can achieve this by using below solution
var array = [{ fromX: { id: 1 }, toX: { id: 2 } }, { fromX: { id: 3 }, toX: { id: 4 } }],
arrayX = array.map(x => x.fromX), arraytoX = array.map(toX => toX.toX)
console.log(arrayX);
console.log(arraytoX );