How to chain get and map the result with lodash? - lodash

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!

I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Related

Error: Exception in HostFunction: Attempting to create an object of type 'sets' with an existing primary key value '6' in react native

I'm trying to store history of workout in realm, my addHistory function looks like this
export function addHistory(workout, exercise, sets, _id) {
console.log({
workout,
exercise,
sets,
_id,
});
if (
_id !== undefined &&
workout !== undefined &&
exercise !== undefined &&
sets !== undefined
) {
// return console.log("HISTORY ", { workout, exercise, sets, _id });
return realm.write(() => {
return realm.create("workoutData", {
_id: _id,
exercise,
workout,
sets,
workoutDate: new Date(Date.now()),
});
});
} else {
alert("History is incomplete");
}
}
Schema of the workoutData is as follows:
exports.workoutData = {
name: "workoutData",
primaryKey: "_id",
properties: {
_id: "int",
workout: "workouts",
exercise: "exercise",
workoutDate: "date",
sets: "sets[]",
},
};
Now when I add sets and click on finishWorkoutHandler the logic works fine before the addHistory function but when addHistory is executed it throws the error as stated in the question.
//finish workout handler
const finishWorkoutHandler = () => {
if (sets.length == 0) {
return;
}
let setsFromRealm = realm.objects("sets");
let workoutData = realm.objects("workoutData");
let setsArray = [];
exercises.forEach((exercise) => {
sets
.filter((items) => items.exercise._id == exercise._id)
.forEach((sets) => {
let _id = 0;
if (setsFromRealm.length > 0) {
_id = realm.objects("sets").max("_id") + 1;
}
addSet(
sets.name,
parseInt(sets.weight),
parseInt(sets.reps),
parseInt(sets.rmValue),
sets.isHeighest,
sets.exercise,
_id,
sets.profile,
sets.failedSet,
sets.warmupSet,
sets.notes
);
let indiSet = {
name: sets.name,
weight: parseInt(sets.weight),
reps: parseInt(sets.reps),
rmValue: parseInt(sets.rmValue),
isHeighest: sets.isHeighest,
_id: _id,
profile: sets.profile,
failedSet: sets.failedSet,
warmupSet: sets.warmupSet,
notes: sets.notes,
createdDate: new Date(Date.now()),
};
setsArray.push(indiSet);
});
let workoutDataId = 0;
let setsArrcopy = setsArray;
console.log("SETS ", realm.objects("sets"));
console.log("SETS ", setsArrcopy);
if (workoutData.length > 0) {
workoutDataId = realm.objects("workoutData").max("_id") + 1;
}
**WORKING AS EXPECTED TILL HERE**
// problem lies here
addHistory(params.workout, exercise, setsArrcopy, workoutDataId);
});
dispatch(setsEx([]));
goBack();
};
the structure of setsArrCopy containing sets is as follows
[
({
_id: 6,
createdDate: 2022-09-29T16:27:06.128Z,
failedSet: false,
isHeighest: false,
name: "Thai",
notes: "",
profile: [Object],
reps: 12,
rmValue: 64,
warmupSet: false,
weight: 56,
},
{
_id: 7,
createdDate: 2022-09-29T16:27:06.151Z,
failedSet: false,
isHeighest: false,
name: "Thsi 3",
notes: "",
profile: [Object],
reps: 10,
rmValue: 75,
warmupSet: false,
weight: 66,
})
];
the logic is also working fine in terms of assigning new ids to the sets being added in a loop. But somehow its throwing error when passing setArrCopy to addHistory function. Although its an array of sets not a single object?

Vue.js with iView Table - accessing Elasticsearch search response object

I am using the iView UI kit table in my Vue.js application that consumes an Elasticsearch API with axios. My problem is that I just can't seem to get to access the nested search response object, which is an array list object. I only get to access the 1st level fields, but not the nested ones. I don't know how to set the table row key in the iView table.
This is how my axios call and mapper methods look like:
listObjects(pageNumber){
const self = this
self.$Loading.start()
self.axios.get("/api/elasticsearch/")
.then(response => {
self.ajaxTableData = self.mapObjectToArray(response.data);
self.dataCount = self.ajaxTableData.length;
if(self.ajaxTableData.length < self.pageSize){
self.tableData = self.ajaxTableData;
} else {
self.tableData = self.ajaxTableData.slice(0,self.pageSize);
}
self.$Loading.finish()
})
.catch(e => {
self.tableData = []
self.$Loading.error()
self.errors.push(e)
})
},
mapObjectToArray(data){
var mappedData = Object.keys(data).map(key => {
return data[key];
})
return mappedData
},
The iView table columns look like this:
tableColumns: [
{
title: 'Study Date',
key: 'patientStudy.studyDate',
width: 140,
sortable: true,
sortType: 'desc'
},
{
title: 'Modality',
key: "generalSeries.modality",
width: 140,
sortable: true
},
...
]
The (raw) Elasticsearch documents look like this:
[
{ "score":1, "id":"3a710fa2c1b3f6125fc168c9308531b59e21d6b3",
"type":"dicom", "nestedIdentity":null, "version":-1, "fields":{
"highlightFields":{
},
"sortValues":[
],
"matchedQueries":[
],
"explanation":null,
"shard":null,
"index":"dicomdata",
"clusterAlias":null,
"sourceAsMap":{
"generalSeries":[
{
"seriesInstanceUid":"999.999.2.19960619.163000.1",
"modality":"MR",
"studyInstanceUid":"999.999.2.19960619.163000",
"seriesNumber":"1"
}
],
"patientStudy":[
{
"studyDate":"19990608"
}
]
}
}
]
And this is how the consumed object looks like:
As you can see, the fields I need to access are within the "sourceAsMap" object, and then nested in arrays.
How can I provide the iView table cell key to access them?
UPDATE:
I now "remapped" my Elasticsearch object before displaying it in the Vue.js table, and it works now. However, I don't think that the way I did it is very elegant or clean....maybe you can help me to do it in a better way. This is my method to remap the object:
getData(data){
let jsonMapped = []
for(let i = 0; i < data.length; i++){
let id = {}
id['id'] = data[i].id
let generalData = data[i]['sourceAsMap']['generalData'][0]
let generalSeries = data[i]['sourceAsMap']['generalSeries'][0]
let generalImage = data[i]['sourceAsMap']['generalImage'][0]
let generalEquipment = data[i]['sourceAsMap']['generalEquipment'][0]
let patient = data[i]['sourceAsMap']['patient'][0]
let patientStudy = data[i]['sourceAsMap']['patientStudy'][0]
let contrastBolus = data[i]['sourceAsMap']['contrastBolus'][0]
let specimen = data[i]['sourceAsMap']['specimen'][0]
jsonMapped[i] = Object.assign({}, id, generalData, generalSeries, generalImage, generalEquipment, patient,
patientStudy, contrastBolus, specimen)
}
return jsonMapped
},
The result is this:
Even though it now works, but how can I optimize this code?
A few functions can help you with your situation
let data = [{
key1: ['k1'],
key2: ['k2'],
key3: [{
subKey1: 'sk1',
subKey2: ['sk2'],
subObject: [{ name: 'John', surname: 'Doe' }],
items: [1, 2, 3, 5, 7]
}]
}];
function mapIt(data) {
if (isSingletonArray(data)) {
data = data[0];
}
for(const key in data) {
if (isSingletonArray(data[key])) {
data[key] = mapIt(data[key][0]);
} else {
data[key] = data[key];
}
}
return data;
}
function isSingletonArray(obj) {
return typeof obj === 'object' && Array.isArray(obj) && obj.length === 1;
}
console.log(mapIt(data));
Outputs:
{
key1: 'k1',
key2: 'k2',
key3: {
subKey1: 'sk1',
subKey2: 'sk2',
subObject: { name: 'John', surname: 'Doe' },
items: [ 1, 2, 3, 5, 7 ]
}
}
You can check it in your browser. mapIt unwraps the singleton arrays into objects or primitives.
But I recommend you to watch out special elastic client libraries for javascript. It could save you from writing extra code.

Knockout-Kendo Chart - remove and add series

My project is MVC 5, I am using the following to generate a chart with multiple series:
HTML:
<button data-bind="click: addItem">Add</button>
<button data-bind="click: removeItem">Remove</button>
<div data-bind="kendoChart2: { title: { text: 'Graph Sample' },
series: seriesConfig,tooltip: {visible: true,template: '#= series.name #: #= value #'} , seriesDefaults: {
type: 'line',style: 'smooth'}}"> </div>
Javascript
var MainViewModel = function () {
var self = this;
this.Systolic = ko.observableArray([]);
this.Diastolic = ko.observableArray([]);
this.HeartRate= ko.observableArray([]);
$.ajax({
type: "GET",
url: '/Charts/GetChart',
contentType: "application/json; charset=utf-8",
async: false,
cache: false,
dataType: "json",
success: function (result) {
//Diastolic
if (result && result.Systolic.length > 0) {
for (var i = 0; i < result.Systolic.length; i++) {
self.Systolic.push(result.Systolic[i].Systolic);
}
};
....
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}});
this.seriesConfig = ko.observableArray([
{name: "Systolic", data: this.Systolic()},
{name: "Diastolic",data: this.Diastolic()}]);
this.addItem = function() {
this.seriesConfig.push({ name: "Heart Rate", data: this.HeartRate() });
};
this.removeItem = function() {
this.seriesConfig.remove({ name: "Diastolic", data: this.Diastolic() });
};
}.bind(this);
ko.kendo.bindingFactory.createBinding(
{
name: "kendoChart",
bindingName: "kendoChart2",
watch: {
data: function(value) {
ko.kendo.setDataSource(this, value);
},
series: function(value) {
this._sourceSeries = value;
this.refresh();
this.redraw();}
}
});
window.viewModel = new MainViewModel();
ko.applyBindings(window.viewModel);
The chart works great, however can't add or remove series?
Note:
the addItem works, I get the value of the new series:
series: function (value) {
alert(value[2].name);
this.seriesConfig = value;
this.refresh();
this.redraw();
}
I also tried load all series then use the following hide a series:
$("#kendoChart").getKendoChart().options.series[1].visible = false;
$("#kendoChart").getKendoChart().redraw();
Does not work, I think the chart name does not register.
I am not familiar with knockout-kendo, just with knockout in general, so if fixing obvious problem as described below will not work, you might need to refresh bindings. Looking at this example however this is not needed, so most likely you got caught by a simple fact that array's remove performs simple == comparison and it fails to find equal object in the array.
Here is a simplified example (although you might know it already, but just in case):
var a="abc";
var b="abc";
var aa = [1,2,3,"a","b","c"];
var data1 = {name: a, data: aa};
var data2 = {name: b, data: aa};
now, comparison a==b returns true and clearly data slots are the same, however data1==data2 is false. That is because it's a different object.
So in your example in removeItem you create and pass a new object to remove, not the one in the array, so == comparison fails and nothing is removed as that newly created object isn't in your observable array.
I suggest comparing the name only similar to item.age < 18 comparison from knockout.js documentation on observable arrays:
this.seriesConfig.remove( function (item) { return item.name == "Diastolic"; } )
I believe, this should do the trick.

Filter data using lodash

How can filter the data with some inner attribute value.
updated_id='1234';
var result= _.map(self.list, function (item) {
// return only item.User_Info.id=updated_id
});
You can use the lodash#matchesProperty variant of lodash#filter to filter out objects that you need using the path of the property. The variant is in the 3rd example of the lodash#filter documentation.
var result = _.filter(self.list, ['User_Info.id', updated_id]);
var self = {
list: [
{ User_Info: { id: '4321' } },
{ User_Info: { id: '4321' } },
{ User_Info: { id: '1234' } },
{ User_Info: { id: '3214' } },
{ User_Info: { id: '2143' } }
]
};
var updated_id = '1234';
var result = _.filter(self.list, ['User_Info.id', updated_id]);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
lodash has a filter method:
const updated_id='1234';
const result= _.filter(self.list, item => item.User_Info.id === updated_id);
Use lodash _.filter method:
_.filter(collection, [predicate=_.identity])
Iterates over elements of collection, returning an array of all elements predicate returns truthy for. The predicate is invoked with three arguments: (value, index|key, collection).
with predicate as custom function
_.filter(myArr, function(o) {
return o.name == 'john';
});
with predicate as part of filtered object (the _.matches iteratee shorthand)
_.filter(myArr, {name: 'john'});
with predicate as [key, value] array (the _.matchesProperty iteratee shorthand.)
_.filter(myArr, ['name', 'John']);

How to get filtered rows from GridX?

I'm using Dojo GridX with many modules, including filter:
grid = new Grid({
cacheClass : Cache,
structure: structure,
store: store,
modules : [ Sort, ColumnResizer, Pagination, PaginationBar, CellWidget, GridEdit,
Filter, FilterBar, QuickFilter, HiddenColumns, HScroller ],
autoHeight : true, autoWidth: false,
paginationBarSizes: [25, 50, 100],
paginationBarPosition: 'top,bottom',
}, gridNode);
grid.filterBar.applyFilter({type: 'all', conditions: [
{colId: 'type', condition: 'equal', type: 'Text', value: 'car'}
]})
I've wanted to access the items, that are matching the filter that was set. I've travelled through grid property in DOM explorer, I've found many store references in many modules, but all of them contained all items.
Is it possible to find out what items are visible in grid because they are matching filter, or at least those that are visible on current page? If so, how to do that?
My solution is:
try {
var filterData = [];
var ids = grid.model._exts.clientFilter._ids;
for ( var i = 0; i < ids.length; ++i) {
var id = ids[i];
var item = grid.model.store.get(id);
filterData.push(item);
}
var store = new MemoryStore({
data : filterData
});
} catch (error) {
console.log("Filter is not set.");
}
I was able to obtain filtered gridX data rows using gridX Exporter. Add this Exporter module to your grid. This module does exports the filtered data. Then, convert CSV to Json. There are many CSV to Json conversion javasripts out there.
this.navResult.grid.exporter.toCSV(args).then(this.showResult, this.onError, null)
Based on AirG answer I have designed the following solution. Take into account that there are two cases, with or without filter and that you must be aware of the order of rows if you have applied some sort. At least this works for me.
var store = new Store({
idProperty: "idPeople", data: [
{ idPeople: 1, name: 'John', score: 130, city: 'New York', birthday: '31/02/1980' },
{ idPeople: 2, name: 'Alice', score: 123, city: 'WÃĄshington', birthday: '07/12/1984' },
{ idPeople: 3, name: 'Lee', score: 149, city: 'Shanghai', birthday: '8/10/1986' },
...
]
});
gridx = new GridX({
id: 'mygridx',
cacheClass: Cache,
store: store,
...
modules: [
...
{
moduleClass: Dod,
defaultShow: false,
useAnimation: true,
showExpando: true,
detailProvider: gridXDetailProvider
},
...
],
...
}, 'gridNode');
function gridXDetailProvider (grid, rowId, detailNode, rendered) {
gridXGetDetailContent(grid, rowId, detailNode);
rendered.callback();
return rendered;
}
function gridXGetDetailContent(grid, rowId, detailNode) {
if (grid.model._exts.clientFilter._ids === undefined || grid.model._exts.clientFilter._ids === 0) {
// No filter, with or without sort
detailNode.innerHTML = 'Hello ' + grid.row(grid.model._cache._priority.indexOf(rowId)).item().name + " with id " +
grid.row(grid.model._cache._priority.indexOf(rowId)).item().idPeople;
} else {
// With filter, with or without sort
detailNode.innerHTML = 'Hello ' + grid.row(grid.model._exts.clientFilter._ids.indexOf(rowId)).item().name + " with id " +
grid.row(grid.model._exts.clientFilter._ids.indexOf(rowId)).item().idPeople;
}
}
Hope that helps,
Santiago Horcajo
function getFilteredData() {
var filteredIds = grid.model._exts.clientFilter._ids;
return grid.store.data.filter(function(item) {
return filteredIds.indexOf(item.id) > -1;
});
}