How to partition array into multiple groups using Lodash? - javascript

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();

Related

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

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.

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);

How to filter an array with RxJS operator

Hello to every one i am new to RxJs and reactive programming i would like to filter an array like this:
let subscription = Rx.Observable.from([{id: 1}, {id: 2}, {id: 3}],[{id: 4}, {id: 5}, {id: 6}]);
if i have one array a i can do this:
let subscription = Rx.Observable.from([{id: 1}, {id: 2}, {id: 3}]);
subscription.filter(x => x.id === 1).subscribe(x => console.log(x));
But how i can to do with the second array?
If you know you'll always have array of arrays you can flatten the array and then run filter:
const o = Rx.Observable.of([{id: 1}, {id: 2}, {id: 3}],[{id: 1}, {id: 2}, {id: 3}])
.concatMap(array => array) // flatten the array into single emissions
.filter(x => x.id === 1)
.subscribe(x => console.log(x));
I'm using .of that accepts multiple arguments. However it takes them as they are unlike the from method that iterates the array.
There are a couple of solutions. The easy way is just to create the right observable.
You could directly create the right observable by concatenating the array in your input of from:
let subscription = Rx.Observable.from([{id: 1}, {id: 3}].concat([{id: 1}]));
You could use Rx.Observable.of which directly takes as many arguments as value in the created Observable and use the spread operator:
let subscription = Rx.Observable.of(...[{id: 1}, {id: 3}].concat(...[{id: 1}]));
You could also merge two different observables:
let subscription = Rx.Observable.from([{id: 1}
.merge(Rx.Observable.from([{ id: 1}]);
There are possibly other solutions that could work like using an array of array and then flatMapping the array.

Lodash Remove objects from array by matching ids array

I have an array of objects like:
var a = [
{id: 1, name: 'A'},
{id: 2, name: 'B'},
{id: 3, name: 'C'},
{id: 4, name: 'D'}
];
And Ids array which i want to remove from array a :
var removeItem = [1,2];
I want to remove objects from array a by matching its ids, which removeItem array contains. How can i implement with lodash.
I Checked lodash's _.remove method, but this need a specific condition to remove an item from array. But i have list of ids which i want to remove.
As you mentioned you need the _.remove method and the specific condition you mention is whether the removeItem array contains the id of the checked element of the array.
var removeElements = _.remove(a, obj => removeItem.includes(obj.id));
// you only need to assign the result if you want to do something with the removed elements.
// the a variable now holds the remaining array
You have to pass a predicate function to .remove method from lodash.
var final = _.remove(a, obj => removeItem.indexOf(obj.id) > -1);
using indexOf method.
The indexOf() method returns the first index at which a given element
can be found in the array, or -1 if it is not present.
You can do it using native javascript using filter method which accepts as parameter a callback function.
var a = [
{id: 1, name: 'A'},
{id: 2, name: 'B'},
{id: 3, name: 'C'},
{id: 4, name: 'D'}
];
var removeItem = [1,2];
a = a.filter(function(item){
return removeItem.indexOf( item.id ) == -1;
});
console.log(a);
But filter method just creates a new array by applying a callback function.
From documentation:
The filter() method creates a new array with all elements that pass
the test implemented by the provided function.
If you want to modify the original array use splice method.
var a = [
{id: 1, name: 'A'},
{id: 2, name: 'B'},
{id: 3, name: 'C'},
{id: 4, name: 'D'}
];
var removeItem = [1,2];
removeItem.forEach(function(id){
var itemIndex = a.findIndex(i => i.id == id);
a.splice(itemIndex,1);
});
console.log(a);

"push()" method returns the length of array instead of array (JavaScript)

I want to make a new array by adding an element to an existing array by "push()" method.
This is the existing array:
let arr = [{label: 1, value: 1}, {label: 2, value: 2}];
This is the element I want to add to the existing array:
{label: 3, value: 3}
So this is the full code with "push()" method:
let arr = [{label: 1, value: 1}, {label: 2, value: 2}];
let newArr = arr.push({label: 3, value: 3});
console.log(newArr); // 3
But push() method returns the length of the new array which is "3" to "newArr" variable. However, what I really want is the actual new array instead of its length for "newArr" variable.
Are there any ways to get the actual new array for "newArr" variable?
With push you are actually mutating the original array. Immutable array extending is only available in ES2015+ (supported by all current, major browsers). You can use the spread operator ...:
const original = [1, 2];
const next = [...original, 3];
console.log(next); // [1, 2, 3]
Also new is a reserved keyword and can't be used as an identifier.
First of all, the new keyword has a specified role in javascript, you can't use it as a variable name.
Reserved keywords in javascript.
Secondly, push method works in situ, you don't have to assign it to a new variable. It won't return a new array, but modify the original one.
var arr = [{label: 1, value: 1}, {label:2, value:2}];
arr.push({label:3, value:3});
console.log(arr);
user7334203,
you wrote that arr.push({label:3, value:3}) mean that your new object push in your array so simply print your array so you got your array.
ex
var a = [{label: 1, value: 1}, {label:2, value:2}],
a.push({label:3, value:3}),
var new = a;
console.log(new)
Don't use const .simply do with arr#push .its enough to add with array
var arr= [{label: 1, value: 1}, {label:2, value:2}]
arr.push({label:3, value:3})
console.log(arr)
A good reference for leaning JS is MDN. If you look at the Array#push spec, you'll see that it mutates the input array, returning the new length of the array.
From the MDN documentation push returns the new length of your array, neither the new array, nor the added value.
The push function alters the array in place, meaning it will change the original array (you don't actually need to re-assign it)!
You could have the result you were expecting though by altering your code to:
arr.push({label:3, value:3});
then assign to a new array like:
const newArr = arr;
You can use "concat()" method to get the actual new array for "newArr" variable like below:
let arr = [{label: 1, value: 1}, {label: 2, value: 2}];
let newArr = arr.concat({label: 3, value: 3});
console.log(newArr);
// [
// {label: 1, value: 1},
// {label: 2, value: 2},
// {label: 3, value: 3}
// ]
This code doesn't need "newArr" variable. The actual new array is assigned to the original variable "arr":
let arr = [{label: 1, value: 1}, {label: 2, value: 2}];
arr = arr.concat({label: 3, value: 3});
console.log(arr);
// [
// {label: 1, value: 1},
// {label: 2, value: 2},
// {label: 3, value: 3}
// ]

Categories