How to convert object to Array in order using Lodash - lodash

Say I have object:
obj {
1:{
index: 3
},
2:{
index: 1
},
3:{
index: 2
},
4:{
index: 0
}
}
I want to convert it into an array but in orders of "index", so the output should be
[ { index: 0 }, { index: 1 }, { index: 2 }, { index: 3 } ]
So like _.values but ability to sort by a property?

You won't find a single method to do everything you want. Instead you should divide your operation into smaller chunks and chain them. So as you wrote:
like _.values but ability to sort by a property
You write about two operations: extract values from a map (using _.values) and then sort the resulting array by a property (this can be done by _.sortBy).
To start a chain of operations in Lodash use _.chain and to materialize a chain use chain.value().
Snippet:
const obj = {
1:{
index: 3
},
2:{
index: 1
},
3:{
index: 2
},
4:{
index: 0
}
};
const result = _.chain(obj)
.values()
.sortBy('index')
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

You really do not need a library for this since in plain JS it is practically the same with just Object.values and then sort:
const obj = { 1:{ index: 3 }, 2:{ index: 1 }, 3:{ index: 2 }, 4:{ index: 0 } }
const result = Object.values(obj).sort((a,b) => a.index - b.index)
console.log(result)
But if you really need lodash then this really is just orderby or sortBy since they work on objects with no need to do the values etc:
const obj = { 1:{ index: 3 }, 2:{ index: 1 }, 3:{ index: 2 }, 4:{ index: 0 } }
const result = _.orderBy(obj, 'index') // or _.sortBy
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Related

Filter array based on each letter in the search string

Take the code below which works fine:
function Test() {
const txt = 'a'
const fruit = [
{ name: 'apple' },
{ name: 'orange' },
{ name: 'kiwi' },
{ name: 'banana' },
{ name: 'lemon' },
]
return (
<FlatList
data={fruit.filter((item) => String(item.name).includes(txt))}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
)
}
export default Test
The search string (txt) is a so the output would be:
apple
orange
banana
So far so good, now I want to be able to type for example ae and get all the items in the array that include both a and e in any order, not just ae together. here is the expected result for const txt = 'ae':
apple
orange
You can do it like this
function Test() {
const txt = 'a'
const fruit = [
{ name: 'apple' },
{ name: 'orange' },
{ name: 'kiwi' },
{ name: 'banana' },
{ name: 'lemon' },
]
const searchFinal = () => {
const newSplitSearch = txt.split("");
const finalArray = []
fruit.map((data) => {
const nameData = data.name;
let shouldBeAdded = true;
for (let i = 0; i < newSplitSearch.length; i++) {
if (nameData.includes(newSplitSearch[i])) {
} else {
shouldBeAdded = false;
}
}
if (shouldBeAdded) {
finalArray.push(data)
}
})
return finalArray
}
return (
<FlatList
data={searchFinal()}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
)
}
export default Test
Hope it helps :)
One way is to split the word characters and then on the filter check all of them with array.prototype.every
const txt = 'ae'
const fruit = [
{ name: 'apple' },
{ name: 'orange' },
{ name: 'kiwi' },
{ name: 'banana' },
{ name: 'lemon' },
]
const search = (arr, str) => {
const chars = str.split('');
return arr.filter(
item => chars.every(char => item.name.includes(char)) );
}
console.log(search(fruit, txt) )
Unfortunately the .includes() method of the String object just accepts a string-type parameter to search for.
What you can do however is using a regular expression instead.
Most basically this would be:
console.log("apple".match(/[a|e]/g));
The above would look for the letters a and e and return an array with the results found for apple ["a", "e"]. Problem is it would also return something if there is three times a and no e like in banana.
To overcome this issue we need to clean the returned array from duplicates first and afterwards check if the number of elements is at least the number of unique letters we want to look for - which is 2 in case of a and e.
Here's an example:
const fruit = [{
name: 'apple'
},
{
name: 'orange'
},
{
name: 'kiwi'
},
{
name: 'banana'
},
{
name: 'lemon'
},
]
let txt = "ae";
let regex = new RegExp("[" + txt.split("").join("|") + "]", "g");
fruit.forEach(item => {
console.log(item.name, [...new Set(item.name.match(regex))].length >= txt.length);
})

Unable to copy an array to an array using vue.js

I have a multiple selectbox using vuetify select component. I'm getting array of selected items and I want to merge status as true of selected items like [ { "ward": 1, "status": true }, { "ward": 2, "status": true} ]
I'm trying to copy selected items of array to another array but couldn't succeed. In console, I got selectedFruits as below.
methods: {
save() {
console.log(this.selectedFruits);
debugger;
this.selectedIndex.push({ ward: this.selectedFruits, status: true });
console.log(this.list);
},
You can try this
save(){
this.list = []
this.selectedFruits.forEach(e => {
this.list.push({ ward: e, status: true });
});
console.log(this.list)
}
If you want to copy an array or object without passing it by reference, do it like this
const newArray = JSON.parse(JSON.stringify(oldArray));
You can use the Spread-Operator for this.
Here an example:
const existingArr = [{ hi: 'foobar' }];
const arr = [{ a: 'foo', b: 'bar'}, { a: 'hello', b: 'world'}]
const newArr = [...existingArr, ...arr]
console.log(newArr)

How to Merge two array of object with the same keys that have the same values and the different value will be stored in an array?

I got data from database like this:
[
{
id_user:1,
expense:3000
},
{
id_user:1,
expense:5000
},
{
id_user:2,
expense:35000
},
{
id_user:3,
expense:50100
}
]
How can i convert to json like this ?
[
{
id_user:1,
expense:[3000,5000]
},
{
id_user:2,
expense:35000
},
{
id_user:3,
expense:50100
}
]
Reduce & Map is way to go. And it's way better to have all expense fields to be an array, so you don't have to check weather it's an array or not
const sample = [{
id_user: 1, expense: 3000
}, {
id_user: 1, expense: 5000
}, {
id_user: 2, expense: 35000
}, {
id_user: 3, expense: 50100
}]
const parseData = data => {
return [...data.reduce((acc, { id_user, expense }) => {
const currentUser = acc.get(id_user)
const newExpense = currentUser ? currentUser.expense : []
newExpense.push(expense)
acc.set(id_user, { id_user, expense: newExpense })
return acc
}, new Map()).values()]
}
console.log(parseData(sample))
You would just need to write a program to do it. This is how I'd approach it:
// phase 1
const combined = {};
for (const item of data) {
if (!combined[item.id_user]) {
combined[item.id_user] = [];
}
combined[item.id_user].push(item.expense);
}
// phase 2
const newData = [];
for (const id of Object.keys(combined)) {
newData.push({id_user: id, expense: combined[id]});
}
The approach is 2 phases. The first phase goes through the data and combines expense values for the same user into an array. I build it into an Object since it's efficient to look up entries by their key this way. If there are a lot of records to process this will be faster than pushing into an Array and using .includes to find entries that already exist.
The 2nd phase is building the new array with the combined expense arrays. This is done by pushing each new object into the output Array.
Note that I chose to make the new expense always be an array even if there's only one expense. It's cleaner to have an attribute have a consistent type rather than having to check if it's an Array when accessing the data.

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.

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