Extracting a field value from a nested array of arrays with objects - javascript

Been trying to extract values from fields with no luck. Tried using the map function by nesting it, as well as concating the fields to try and achieve my result but with no luck.
I am looking to take an object with an almost identical structure to this:
[{
name: 'sean',
age: 26,
address:
[{
street: 'red'
},
{
street: 'blue'
}]
}];
From this structure, I want to be able to extract the street value and insert it into a new array. So the result should be:
const newArray = ['red', 'blue'];
The address field can have multiple objects inside of it, so the solution is able to scale for 1-to-many.

const input = [
{
name: 'sean',
age: 26,
address: [
{street: 'red'},
{street: 'blue'}
]
},
{
name: 'foo',
age: 1,
address: [
{street: 'yellow'},
{street: 'green'}
]
}
];
const newArray = input.flatMap(({address}) => address.map(({street}) => street));
console.log(newArray);
Array.prototype.flatMap takes a callback function returning an array of values, and then returns an array of all the values inside the arrays returned by the callback. In other words, array.flatMap(callback) is semantically equivalent to array.map(callback).flat().
You mentioned trying to use reduce and concat in your comment. However, according to the MDN documentation:
var arr = [1, 2, 3, 4];
arr.flatMap(x => [x, x * 2]);
// is equivalent to
arr.reduce((acc, x) => acc.concat([x, x * 2]), []);
// [1, 2, 2, 4, 3, 6, 4, 8]
Note, however, that this is inefficient and should be avoided for large arrays: in each iteration, it creates a new temporary array that must be garbage-collected, and it copies elements from the current accumulator array into a new array instead of just adding the new elements to the existing array.

Related

Accessing duplicates in objects in the same array?

I have an array with multiple objects
arr = [
{name: 'xyz',
age: 13,
},
{name: 'abc',
age: 15,
},
{name: 'abc',
age: 15,
}]
how do I find the duplicate in this array and remove the object that is duplicated in the array? They are all in one array.
Apologies. I just realized what I am trying to do is, remove the object entirely if there's a duplicate in one key... so if the age is similar, I will remove object name "def". Is this possible?
arr = [
{name: 'xyz',
entry: 1,
age: 13,
},
{name: 'abc',
entry: 2,
age: 15,
},
{name: 'def',
age: 13,
entry: 3
}]
You could achieve this by the following steps:
transform each element into an object that is key-sorted, this will make objects consistent in terms of key-value pairs order, which will help us in the next step
map the array into JSON-stringified value, and store it into a Set(), which would help us store only unique stringified objects
turn the Set() back into array
map the array back into objects by JSON.parse() each element
const arr = [
{ name: "xyz", age: 13 },
{ age: 15, name: "abc" },
{ name: "abc", age: 15 },
]
const sortKeys = obj =>
Object.fromEntries(
Object.entries(obj).sort((keyValuePairA, keyValuePairB) =>
keyValuePairA[0].localeCompare(keyValuePairB[0])
)
)
const res = Array.from(
arr
.map(sortKeys)
.map(el => JSON.stringify(el))
.reduce((set, el) => set.add(el), new Set())
).map(el => JSON.parse(el))
console.log(res)
References
Set
Object.entries()
Object.fromEntries()

Transform array of objects to nested objects using Ramda

I am a newbie so pardon me if I ask a naive question.
I have an array of objects
const arr = [{id: 1, name: 'Pete'}, {id: 5, name: 'John'}, {id: 3, name: 'Peter'}]
and I want to convert it to this form using ramda:
const obj = {1 : {id: 1, name: 'Pete'}, 5: {id: 5, name: 'John'}, 3: {id: 3, name: 'Peter'}}
Can anyone please help?
Other Conceptual Questions:
I want to convert nested array of objects to this form because that way searching a name will be quick if id is given. Is this the right approach?
Is there any other efficient way performance wise that can make the search in array quicker?
Thanks
You can use ramda's R.indexBy:
const arr = [{id: 1, name: 'Pete'}, {id: 2, name: 'John'}, {id: 3, name: 'Peter'}]
const result = R.indexBy(R.prop('id'))(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
I want to convert nested array of objects to this form because that
way searching a name will be quick if id is given. Is this the right
approach?
Getting an item from array is usually O(n). Getting an item from an object (dictionary), by the property is indexed by is O(1), so object wins... if you want to get the name by the id. However, if you are looking for an object by name, you should index by the name property. In addition, are you going to look for exact names? or names that contain something. If the search is for a part of name, then you'll still need to iterate everything O(n), and array should be fine as well.
Is there any other efficient way performance wise that can make the
search in array quicker?
It actually depends on how you are going to search, and the amount of items you are going to search through, if you are under about 50,000 and searching hy id - an object or a Map would be fine, by part of a name - an array would be ok. However, don't try to optimize before you actually have a problem, you profile it, and find that the search is to blame.
Without ramda, you could use build-in Object.fromEntries with a mapping of id and object.
const
array = [{ id: 1, name: 'Pete' }, { id: 2, name: 'John' }, { id: 3, name: 'Peter' }],
object = Object.fromEntries(array.map(o => [o.id, o]));
console.log(object[2].name);
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Map here for better performance.
const map = new Map();
const arr = [{id: 1, name: 'Pete'}, {id: 2, name: 'John'}, {id: 3, name: 'Peter'}]
for(const {id, name} of arr){
map.set(id, name);
}
//check id exists
map.has(1) // true
//get person's name
map.get(1) //"Pete"
You could try a filter - this leaves the original array intact and returns a new array with the matches:
const arr = [{id: 1, name: 'Pete'}, {id: 2, name: 'John'}, {id: 3, name: 'Peter'}]
let filtered = arr.filter(a => a.id == 1);
console.log(filtered);

Filter an array by values which aren't a property value in an array of objects - Javascript

I have an array of values, for example:
let values = [1, 2, 3, 4, 5];
And I have another array, containing objects, for example:
let objects = [{name: 'Dennis', value: 2}, {name: 'Charlie', value: 4}];
I would like to produce an array that only contains values that aren't present in the value property of my objects.
So from the examples above, I would return an array of [1, 3, 5]
What I've got at the moment is:
let missingValues = [];
for (let i = 0; i < values.length; i++) {
if (!objects.filter(obj => obj.value === values[i]) {
missingValues.push(values[i]);
}
}
This works, but feels slightly messy, is there a more efficient way to do this?
You could filter the values with a look up in the objects array.
var values = [1, 2, 3, 4, 5],
objects = [{ name: 'Dennis', value: 2 }, { name: 'Charlie', value: 4 }],
result = values.filter(v => !objects.find(({ value }) => value === v));
console.log(result);
...is there a more efficient way to do this?
I assume you really mean "efficient" (as opposed to "concise").
Yes, you can build an object or Set containing the values from the second array, and then use that to filter instead:
let values = [1, 2, 3, 4, 5];
let objects = [{name: 'Dennis', value: 2}, {name: 'Charlie', value: 4}];
let valueSet = new Set();
for (const obj of objects) {
valueSet.add(obj.value);
}
let missingValues = values.filter(value => !valueSet.has(value));
console.log(missingValues);
(You can also use an object for the same thing, if you prefer. let valueSet = Object.create(null); then valueSet[value] = true and !valueSet[value].)
This is only worth doing if the product of the arrays is quite large (hundreds of thousands of entries) or you're doing this a lot.

How to partition array into multiple groups using Lodash?

I'm trying to find a concise way to partition an array of objects into groups of arrays based on a predicate.
var arr = [
{id: 1, val: 'a'},
{id: 1, val: 'b'},
{id: 2, val: 'c'},
{id: 3, val: 'a'}
];
//transform to below
var partitionedById = [
[{id: 1, val: 'a'}, {id: 1, val:'b'}],
[{id: 2, val: 'c'}],
[{id: 3, val: 'a'}
];
I see this question , which gives a good overview using plain JS, but I'm wondering if there's a more concise way to do this using lodash? I see the partition function but it only splits the arrays into 2 groups (need it to be 'n' number of partitions). The groupBy groups it into an object by keys, I'm looking for the same but in an array (without keys).
Is there a simpler way to maybe nest a couple lodash functions to achieve this?
You can first group by id, which will yield an object where the keys are the different values of id and the values are an array of all array items with that id, which is basically what you want (use _.values() to get just the value arrays):
// "regular" version
var partitionedById = _.values(_.groupBy(arr, 'id'));
// chained version
var partitionedById = _(arr).groupBy('id').values().value();

Concatenate an unknown number of arrays in Javascript/NodeJS

I have a function in one of my controllers where I populate an array of references to a document, which, when populated, have embedded arrays themselves.
Here's an example:
The mongoose populate function gives me an array of objects. Within each of those objects is an array:
[{ name: Test, array: [ 1, 2, 3, 4, 5 ] }, { name: TestAgain, array: [1, 2, 3, 4, 5] }, { name: Test^3, array: [1, 2, 3, 4, 5]}, {...
The desired output would be:
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...]
I need to concatenate all of the "arrays" within the populated references. How can I do this without knowing how many arrays there are?
For reference, here is (generally) what my function looks like:
exports.myFunctionName = function ( req, res, next )
Document.findOne({ 'document_id' : decodeURIComponent( document_id )}).populate('array_containing_references').exec(function( err, document)
{
//Here I need to concatenate all of the embedded arrays, then sort and return the result as JSON (not worried about the sorting).
});
Assuming your input is in the document variable, try this:
var output = document.reduce(function (res, cur) {
Array.prototype.push.apply(res, cur.array);
return res;
}, []);
Or this:
var output = [];
document.forEach(function(cur) {
Array.prototype.push.apply(output, cur.array);
});
You want to take each document and do something with a property from it. Sounds like a great use case for Array.prototype.map!
map will get each document's array value and return you an array of those values. But, you don't want a nested array so we simply use Array.prototype.concat to flatten it. You could also use something like lodash/underscore.js flatten method.
var a = [
{ name: 'test1', array: [1, 2, 3, 4, 5]},
{ name: 'test2', array: [1, 2, 3, 4, 5]},
{ name: 'test3', array: [1, 2, 3, 4, 5]}
];
var results = Array.prototype.concat.apply([], a.map(function(doc) { return doc.array; }));
document.body.innerHTML = results;

Categories