lodash: create groups from array of properties in array of objects - javascript

I've recently been using lodash and I just love it. So I'm on a quest to learn it better.
For the past two days I've been trying to figure out how to do the following, without much success: considering an array of objects each with an array of objects in a property props...
let items = [{
name: 'first',
props: [{
id: 1,
name: 'one'
}, {
id: 2,
name: 'two'
}]
}, {
name: 'second',
props: [{
id: 2,
name: 'two'
}]
}, {
name: 'third',
props: [{
id: 1,
name: 'one'
}]
}];
... I'd like to create an array of objects containing each property in props across all items, each having an items property with all items containing that property, like this:
[{
id: 1,
name: 'one',
items : [] // array containing objects `first` and 'third'
},{
id: 2,
name: 'two',
items: [] // array containing objects `first` and 'second'
}]
I've tried various combinations inspired from answers of this question, which seems the closest I could find to what I'm trying to do.I think my real blocker is that, when trying to create an array of unique props (to which I'm going to map the items) and trying to use...
_(items).chain().flatten().pluck('props').unique().value()
... as in this answer, it tells me .chain(...).flatten(...).pluck is not a function. I also tried with .map instead of .pluck, but I guess not everything working in Underscore works in Lodash (I don't know the specific differences, I just read Lodash was forked from Underscore initially).
Note I know how to do this with classic fors and this is only an exercise. I don't have a practical use for it.
I'm just trying to learn to use lodash to its full potential. If you know how to do it, you don't even have to explain. I'll console.log() each step of the way until I understand what's going on. Also, if you think my question's name could be improved, for better indexing, considering what I want to achieve, I'm open to suggestions.
Ideally, the answer should be a chain of lodash methods, without declaring intermediary variables. I'm pretty sure it's doable (that's why I'm asking). Of course, the fewer steps the better — I assume it would impact performance in a real life scenario, but this is not a concern here — I'm aware that most times, when going for performance, the classic for loop is hard to beat.

First, let's transform each item to an array of props with the item's name, flatten this array of arrays, then the rest should be easier.
_(data)
.chain()
.flatMap(({ props, name }) => _.map(props, p => ({
id: p.id,
name: p.name,
item: name
})))
.groupBy(p => p.id)
.map(props => ({
id: _.head(props).id,
name: _.head(props).name,
items: _.map(props, p => p.item)
}))
.value();

Related

Executing a list of functions on an array

I'm getting to grips with using functional programming beyond a simple map or two. I have a situation where I want to be able to filter some elements from an array of objects, based on a particular field of those objects. It's a contrived example, but here goes:
I have a list of field definitions, and I want to extract two of them based on their title.
const toSearch = [
{ title: "name", description: "Their name" },
{ title: "age", description: "Their age" },
{ title: "gender", description: "Their gender" }
]
const fieldsToFind = ["name", "age"]
let filterObjects = R.map(R.objOf('title'), fieldsToFind)
let filterFuncs = R.map(R.whereEq(R.__), filterObjects)
let found = R.map(R.filter, filterFuncs)
console.log("Filter objects:", JSON.stringify(filterObjects))
console.log("Filter functions:", JSON.stringify(filterFuncs))
console.log("Found:", found[0](toSearch))
console.log("Found:", found[1](toSearch))
If I run this, the last of the output is the two elements of toSearch that I'm looking for, but it's not exactly neat. I've been trying to get another map working to get around executing found elements manually, but I also feel that even leading up to that point I'm taking an overly circuitous route.
Although it's a contrived example, is there a neater way of accomplishing this in a functional style?
One fairly simple way of doing this is:
R.filter(R.where({R.title: R.contains(R.__, ['name', 'age'])}))(toSearch);
//=> [
// {"description": "Their name", "title": "name"},
// {"description": "Their age", "title": "age"}
// ]
or, equivalently,
R.filter(R.where({title: R.flip(R.contains)(['name', 'age'])}))(toSearch);
One advantage, especially if you import the relevant functions from R into your scope, is how closely this reads to your problem domain:
var myFunc = filter(where({title: contains(__, ['name', 'age'])}));
myFunc = filter where the title contains 'name' or 'age'.
You can see this in action on the Ramda REPL.
I have a situation where I want to be able to filter some elements
from an array of objects, based on a particular field of those
objects.
For your situation, its quite simple
var filteredItems = toSearch.filter(function(value){ return fieldsToFind.indexOf(value.title) != -1 });
You could use .reduce to accomplish your task. MDN has a very brief explanation of the reduce function

Angularjs - How to traverse, add and delete elements in nested arrays

I have a nested array:
$scope.audits = [{
name: '2015',
sections:[{
section:'mm',
id:'1',
subsections: [{
subsection:'aa',
mainelements: ['cc','dd']
},{
subsection:'bb',
mainelements: ['ee','ff','gg']
}]
}]
}];
I want to have a few buttons to add and delete nested elements at will. I cannot use $index because it is multiple layers of array.
Here is my Plunker
This problem got it's crazy solutions out here: AngularJS: traversing nested arrays . I'm not going to rewrite the solutions once again because it's unefficient. Don't forget to use search before asking a question.

Javascript function to count nested array of object properties possibly using reduce/map

I have a problem and I have looked at a few reduce questions on here and I just can't seem to wrap my head around a way to do this. I have an array of objects. Each object has the same properties, about 30 of them. Each of the properties has completely different types of information. I want to be able to create a new object/array easily. My example is below.
var array = [{parts: 12345, purchased: "yes", customerId: 12541},
{parts: 12432, purchased: "no", customerId: 55514},
{parts: 12345, purchased: "Yes", customerId: 44421}];
What I want to do is find a way to extract useful data depending on the type of information in the array. For example:
some_function(array) {
...
return { parts: {uniquePart: 12345, timesListed: 2}};
}
}
I may also want to extend that returned object and count the number of times purchased was either yes or no. I have tried numerous approaches but I am thinking this is more a data model issue than a programming issue.
I am parsing this data off of strings of receipts. Once the plain text string is parsed I get a large 30 property object for each transaction. Some of the properties also are nested objects or arrays as well.
I want to correlate data across multiple transactions. Maybe I need to research a better way to approach this type of situation as a whole.
So I understand the question is a little vague but what I really want to know is what is the best way with the array given to end up with the following data structure:
{parts: {uniquePart: 12345, timeListed 2}}
I believe once I understand how to itterate through the nested array of objects and build the new object I can go from there. My current attempts using reduce have not yielded fruit.
array.reduce(acc,obj){return This is where I am a little lost}
This solution features Array.prototype.forEach, Object.keys and Array.prototype.map for a temporary object count and returns the wanted array with one object for every part.
function getCount(array) {
var count = {};
array.forEach(function (a) {
count[a.parts] = (count[a.parts] || 0) + 1;
});
return Object.keys(count).map(function (k) {
return { parts: { uniquePart: k, timesListed: count[k] } };
});
}
var array = [{ parts: 12345, purchased: "yes", customerId: 12541 }, { parts: 12432, purchased: "no", customerId: 55514 }, { parts: 12345, purchased: "Yes", customerId: 44421 }];
document.write('<pre>' + JSON.stringify(getCount(array), 0, 4) + '</pre>');

Can this Ramda.js data transformation be improved?

I have a list of stores and a list of ids:
var stores = [{id: '1', name: 'first store'}, {id: '2', name: 'second store'}, {id: '3', name: 'third store'}];
var ids = ['1', '2'];
I want to obtain the names of stores that match the ids on the list:
["first store", "second store"]
Here's what I came up with:
var filterStoresById = R.intersectionWith(R.useWith(R.equals, R.view(R.lensProp('id'))));
var extractNames = R.map(R.view(R.lensProp('name')));
extractNames(filterStoresById(stores,ids));
I'm aiming to learn functional programming concepts while at the same time producing code I could use on real life but I don't think my solution is readable nor performant (I'm iterating twice), so what improvements could be made to this code?
Your code is not horrible, only a little less readable than it might be.
First of all, to access a top-level property, especially when you're not reusing the access method, it's much simpler to use prop(name) than view(lensProp(name)). And Ramda has pluck for map(prop).
Second, I find it much easier to read functions built through functional composition than through nested levels of parentheses. I generally prefer pipe to compose, but either would do.
So I would rework your code like this:
var filterStoresById = R.intersectionWith(R.useWith(R.equals, R.prop('id')));
var storeNamesById = R.pipe(filterStoresById, R.pluck('name'));
storeNamesById(stores, ids);
One advantage to this is that you now have the reusable filterStoresById function if you find that you need more than just the names.
The other question is one of performance. There is definitely a penalty for iterating twice. The question is whether it is worth it for the cleaner, more easily refactored code. There are techniques for turning code like this into code that does the same thing but iterating only once and avoiding intermediate containers. You can see these articles on transducers for more information.
But I would avoid worrying about this unless you can demonstrate to yourself that this is actually a performance problem in your application. I assume everyone knows the Knuth misquote "premature optimization is the root of all evil."
innerJoin FTW
const stores = [
{id: '1', name: 'first store'},
{id: '2', name: 'second store'},
{id: '3', name: 'third store'},
]
const ids = ['1', '2']
const f =
R.pipe(R.innerJoin(R.flip(R.propEq('id'))), R.pluck('name'))
console.log(f(stores, ids))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

complicated custom sorting in nested array

I've problem sorting my nested array, says I've json like this
var orders = [{
'orderId': 1,
'sales': [{
'salesNumbers': 3
}]
}, {
'orderId': 2,
'sales': [{
'salesNumbers': 4
}]
}];
and I wish I can sort orderId base on salesNumbers. You may say it's impossible or I made a mistake by putting sales as array but it contain only 1 object which is salesNumbers. That's not a mistake, I just do not want to simplify my problem.
so it's possible to, without changing the data structure, sort orderId base on salesNumbers?? My app demo http://jsfiddle.net/sq2C3/
Since you say the sales array only has one item in it, you can order by salesNumbers like this:
orderBy:'sales[0].salesNumbers'
Here is an update of your fiddle: http://jsfiddle.net/wittwerj/sq2C3/2/

Categories

Resources