I have an array like ['A','B','F'] and an array of objects
[
{
name:'A',
prop1:'value1'
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
]
I want to filter out objects where name exists in array and add property exists:true to the array of objects and return the updated array of objects.
So for the above case it should return
[
{
name:'A',
prop1:'value1',
exists:true
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
]
I suppose it could be done with help of JavaScript Sets but not sure how. Also can this be done in linear complexity?
To achieve linear time complexity, you can turn your character array into a Set. Now just simply loop through the array of objects.
const characters = ['A','B','F'];
const objects = [
{
name:'A',
prop1:'value1'
},
{
name:'C',
prop3:'value3'
},
{
name:'E',
prop7:'value7'
},
];
const charSet = new Set(characters);
const output = objects.map(obj => {
if (charSet.has(obj.name)) obj.exists = true;
return obj;
});
This takes O(n + m), where n is the length of the character array, and m is the length of the object array.
Related
I have an array of object as follows. Currently there is only one object but invocations has an array of objects.
Depending on how many array of objects we have for invocations, we want to update the main array of objects. For e.g. if there are 2 objects in invocations, i want to separate those 2 invocations and replicate their parent object for both invocations.
This is not a regular iteration of array of objects and thats why i am not able to get the desired result. Any help is appreciated
const input = [
{
"name": "Test Data",
"invocations": [
{ "invocationId": "123" },
{ "invocationId": "125" },
]
},
]
const output = [
{
"name": "Test Data",
"invocations": [
{ "invocationId": "123" },
]
},
{
"name": "Test Data",
"invocations": [
{ "invocationId": "125" },
]
}
]
Here is a working example non-one-liner though!
For each object inside your array, you loop through the invocations property array and push each object inside a temp array along with all the other properties.
Then this array is concatenated with the final array.
const input = [
{
"name": "Test Data",
"invocations": [
{ "invocationId": "123" },
{ "invocationId": "125" },
]
},
]
let finalArray = []
Object.values(input).forEach(obj => {
let arr = []
obj.invocations.forEach(invoc => arr.push({...obj, invocations: [ invoc ]}))
finalArray = finalArray.concat(arr)
});
console.log(finalArray)
This will do it:
const splitInvocations = array => {
return array.reduce((result, obj) => {
const parentAssignedObjects = obj.invocations.map(invocation => ({ ...obj, invocations: [invocation] }));
return [...result, ...parentAssignedObjects];
}, []);
};
First, we loop through the array with reduce. We then map each invocation and return the parent object with a replaced value for the property invocations. We concat the reduce accumulator with the output of map using spread syntax.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
I am having what I think is a pretty trivial problem but somehow I can't find a solution to. I have a response body that looks like this:
{
"sizes": [
{
"43": 35
},
{
"42": 20
},
{
"38": 10
}
]
}
where the keys are shoe sizes and the value is quantity of each size. How do I access the sizes? What I currently have is this:
const sizesArray = response.data.sizes
const arr = Object.values(msizes);
console.log('arr', arr);
arr.map((i,a) => {
console.log('i',i);
console.log('a',a);
})
but i is then again a object {43: 35}
and a is just the index. I want somehow to assign the key to parameter called 'sizes' and the key to a parameter called quantity.
You can use Object.keys, a bit simpler than Object.entries
Example:
const data = { sizes: [{ "43": 35 }, { "42": 20 }, { "38": 10 }] };
const result = data.sizes.map((element, index) => {
let obj = Object.keys(element); // returns an array of keys
let key = obj[0]; // first element is the only key
let quantity = element[key]; // bracket notation, key is an string, not number
console.log("size", key);
console.log("quantity", quantity);
});
You can just iterate the sizes array, using reduce to append the keys of each object to an output array of sizes:
const data = { sizes: [{ "43": 35 }, { "42": 20 }, { "38": 10 }] }
const sizes = data.sizes.reduce((acc, s) => acc.concat(Object.keys(s)), [])
console.log(sizes)
If you want sizes and quantities, you can take a similar approach, just generate an object which accumulates both sets of values:
const data = { sizes: [{ "43": 35 }, { "42": 20 }, { "38": 10 }] }
const { sizes, quantities } = data.sizes
.reduce((acc, s) => {
acc.sizes = acc.sizes.concat(Object.keys(s))
acc.quantities = acc.quantities.concat(Object.values(s))
return acc
},
{ sizes : [], quantities : [] })
console.log(sizes)
console.log(quantities)
You were on the right track :)
Use Object.keys() to get an array of your keys (shoe-sizes). Then use the map()-function to create a new array. Use the index of map() to access the quantity in your response.
const sizesArray = response.data.sizes
const sizes = Object.keys(sizesArray);
const result = sizes.map((element, index) => ({
size: element,
quantity: sizesArray[index]
}));
console.log(result);
I'm working on an array using groupBy lodash method, here the
original array order does not exist on performing groupBy, is the any
way to regain it after groupBy()
const groupBy from "groupBy/lodash";
const mockData = [
{ groupId:"44", groupName:"mno", },
{ groupId:"45", groupName:"pqr" },
{ groupId:"40", groupName:"abc" },
{ groupId:"41", groupName:"def" },
]
const filteredArray = mockData.filter(item =>....); //logic to filter based on few parameters
const newArray = groupBy(filteredArray, "groupId");
console.log(newArray);
result - {
40: [{ groupId:"40", groupName:"abc" }],
41: [{ groupId:"41", groupName:"def" }],
44: [{ groupId:"44", groupName:"mno" }],
45: [{ groupId:"45", groupName:"pqr" }],
}
expected - {
44: [{ groupId:"44", groupName:"mno", }],
45: [{ groupId:"45", groupName:"pqr" }],
40: [{ groupId:"40", groupName:"abc" }],
41 [{ groupId:"41", groupName:"def" }],
}
My guess, the groupBy method is sorting/grouping the array using groupId.
Can you clarify this question, are you saying the mockData order is diff and newArray order is diff. If so, then it is groupedBy groupId otherwise its mutating the array.
The _.groupBy() function creates an object. You are grouping by the id, and the ids are integers in a string form. ES6 traversal order rules order the integer properties by their value (ascending) - see this article. In your case, the groups are sorted by the id integer value.
Since _.groupBy() doesn't actually sort the keys, nor can you sort object's integer keys explicitly, you'll need to use another structure that preserves the order - a Map.
const groupByToMap = (arr, predicate) =>
arr.reduce((acc, o) => {
const key = predicate(o)
if(!acc.has(key)) acc.set(key, [])
acc.get(key).push(o)
return acc
}, new Map())
const mockData = [{ groupId:"44", groupName:"mno" }, { groupId:"45", groupName:"pqr" }, { groupId:"40", groupName:"abc" }, { groupId:"41", groupName:"def" }]
const result = groupByToMap(mockData, o => o.groupId)
// !!! look at the browser's console (the snippet's would display an empty object) !!!
console.log(result)
// when converting to an object the keys would be reordered by their integer value
console.log(Object.fromEntries(result))
Say that I had the following two arrays:
var arrToChangeAndCompare = [{name:'Bob'},{name:'Joe'},{name:'Alise'},{name:'Joe1'}];
var arrToCompare = [{name:'Frank'},{name:'Joe'},{name:'Jen'},{name:'Joe1'}];
And I want to first array to only contain unique names in the array its in, but also unique names compared to the second array (arrToCompare). To be clear, I don't want to REMOVE values that are not unique, I want to CHANGE them in "arrToChangeAndCompare" so they are then unique to both arrays. Keep in mind that if there are non-unique values in "arrToCompare", this does not matter. The only rule is that the "arrToChangeAndCompare" only has unique names in its array and names that are unique compared to "arrToCompare" as well.
I have the following code that almost does what I want, but it doesn't check the second array again once it changes a value in the first array to be unique, which results in the "arr" array to have all unique values, but the "arr" values are not unique in both arrays (Joe1 exists in the first array and the second array).
ALMOST WORKING CODE
var arr = [{name:'Bob'},{name:'Joe'},{name:'Alise'},{name:'Joe1'}];
var arr2 = [{name:'Frank'},{name:'Joe'},{name:'Jen'},{name:'Joe1'}];
var arr3;
arr2.forEach(e2 => {
arr3 = arr.map(e => {
var name = e.name.match(/[a-zA-Z]*/)[0]
if (e2.name === name) {
var number = e.name.match(/[0-9]+/)
if (number) {
e.name = name + (+number[0] + 1)
} else {
e.name = name + 1
}
}
return e
})
})
console.log(arr3)
The expected output of arrays listed at the top would be one array that looks like this:
[
{
"name": "Bob"
},
{
"name": "Joe2"
},
{
"name": "Alise"
},
{
"name": "Joe3"
}
]
EXPLANATION OF OUTPUT
Bob is unique, so no actual is done for the first object in arrToChangeAndCompare
Joe is not unique compared to the arrtoCompare, so its number needs to be changed. But, since Joe1 exists in arrToChangeAndCompare as well, it has to be incremented again to Joe2
Alise is unique to both arrays, so no actual is done for the third object in arrToChangeAndCompare
Joe1 is not unique compared to arrtoCompare, so Joe1 must be incremented to Joe2, But since Joe2 also exists in arrToChangeAndCompare, it must then be incremented to Joe3
All values in arrToChangeAndCompare are now unique compared to its own array and arrtoCompare
Please ask additional questions as needed. Your help is much appreciated!
You could take a Set with name and the letter parts of name and check against.
If not found add string to set and return the value.
If found take letters, digits and increment the numerical value until no more combined string is in the set. Then update the set and return the new string.
var array1 = [{ name: 'Bob', foo: 42 }, { name: 'Bob', bar: 42 }, { name: 'Bob' }, { name: 'Joe' }, { name: 'Joe' }, { name: 'Joe' }, { name: 'Alise' }, { name: 'Joe1' }],
array2 = [{ name: 'Frank' }, { name: 'Joe' }, { name: 'Jen' }, { name: 'Joe1' }],
items = array2.reduce((s, { name }) => s.add(name).add(name.match(/^\D+/)[0]), new Set),
result = array1.map(({ name, ...o }) => {
if (!items.has(name)) {
items.add(name);
return { name, ...o };
}
var [letters, digits = 0] = name.split(/(\d+)/);
digits = +digits;
while (items.has(letters + ++digits));
items.add(letters + digits);
return { name: letters + digits, ...o };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This may be a duplicate, but I'm not sure.
I have the following array:
[
{
id: "object1"
},
{
id: "object2"
},
{
id: "object3"
}
]
The trick is, the array is dynamic, and, therefore, the global IDs of this array objects vary. For example, array[1] in one case may be an object with id "object1", and, in other case, that with an id of "object3".
How to query this array based on the id string and have the array index as the output?
reduce into an object indexed by id, with the values being the index of that id's object in the original array, and then you can use simple object lookup:
const input = [
{
id: "object1"
},
{
id: "object2"
},
{
id: "object3"
}
];
const indexedById = input.reduce((a, { id }, i) => {
a[id] = i;
return a;
}, {});
console.log(indexedById.object2); // index of 1 in input
.findIndex is another possibility, but it has worse time complexity than object lookup.
Array has a findIndex, so you could do const findById = (x) => xs.findIndex(({ id }) => id === x) where x is your id string and xs is your array of objects.