lodash pick object fields from array - javascript

I have array of objects:
var results= [
{
"_type": "MyType",
"_id": "57623535a44b8f1417740a13",
"_source": {
"info": {
"year": 2010,
"number": "string",
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}
}
];
I need to select specific fields from array. In result, I want to get the following:
[
{
"_id": "57623535a44b8f1417740a13",
"info": {
"year": 2010,
"number": "string"
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}
]
As you can see, I want to select _id field and content of _source object. How can I do this with lodash?
I've found .map function, but it doesn't take array of keys:
var res = _.map(results, "_source");

You could do:
var mapped = _.map(results, _.partialRight(_.pick, ['_id', 'info', 'type', 'date', 'createdBy']));
A little explanation:
_.map(): Expects a function which takes each item from the collection so that you can map it to something else.
_.partialRight(): Takes a function which will be called later on with the its arguments appended to the end
_.pick(): Gets the path specified from the object.

In plain Javascript you could iterate with Array#map and assemble a new object for each object without mutilation the original object.
var results = [{ "_type": "MyType", "_id": "57623535a44b8f1417740a13", "_source": { "info": { "year": 2010, "number": "string", }, "type": "stolen", "date": "2016-06-16T00:00:00", "createdBy": "57469f3c71c8bf2479d225a6" } }],
res = results.map(function (a) {
var o = { _id: a._id };
["info", "type", "date", "createdBy"].forEach(function (k) {
o[k] = a._source[k];
});
return o;
});
console.log(res);

I had the same requirement, and the below solution worked best for me.
let users = [
{
"_id": "5ead7783ed74d152f86de7b0",
"first_name": "User First name 1",
"last_name": "User Last name 1",
"email": "user1#example.com",
"phone": 9587788888
},
{
"_id": "5ead7b780d4bc43fd0ef92e7",
"first_name": "User FIRST name 1",
"last_name": "User LAST name 1",
"email": "user2#example.com",
"phone": 9587788888
}
];
users = users.map(user => _.pick(user,['_id','first_name']))
console.log(users)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

var results = [{
_type: "MyType",
_id: "57623535a44b8f1417740a13",
_source: {
info: {
year: 2010,
number: "string",
},
type: "stolen",
date: "2016-06-16T00:00:00",
createdBy: "57469f3c71c8bf2479d225a6"
}
}];
var rootProperty = ['_id']
var innerProperty = '_source'
var myArray = _.map(results, result => _(result)
.pick(rootProperty)
.assign(_.result(result, innerProperty))
.value()
)
console.log(myArray)
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

You can map() the result and have each item assign() the _id key-value in an object toegether with the _source object.
results = _.map(results, item => _.assign(
{ _id: item._id },
item._source
));
var results = [{
"_type": "MyType",
"_id": "57623535a44b8f1417740a13",
"_source": {
"info": {
"year": 2010,
"number": "string",
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}
}];
results = _.map(results, item => _.assign(
{ _id: item._id },
item._source
));
document.write('<pre>' + JSON.stringify(results, 0, 4) + '</pre>');
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
You may also choose to write this in plain JS:
result = results.map(item => Object.assign(
{ _id: item._id }, item._source
));
var results = [{
"_type": "MyType",
"_id": "57623535a44b8f1417740a13",
"_source": {
"info": {
"year": 2010,
"number": "string",
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}
}];
result = results.map(item => Object.assign(
{ _id: item._id }, item._source
));
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

To correctly fulfill the OP's question and for even more complex requirements, the application of a schema and a small lodash mixin is invaluable.
The JavaScript is a little ugly, but it looks swell in CoffeeScript (yes, that was a thing once). The compiled JavaScript is hidden beneath.
_.mixin mapGet: (obj, schema) ->
result = for row in input
row_result = {}
for key, value of schema
row_result[key] = _.get(row, value)
row_result
_.mixin({ mapGet: function(obj, schema) {
var key, result, row, row_result, value;
return result = (function() {
var i, len, results;
results = [];
for (i = 0, len = input.length; i < len; i++) {
row = input[i];
row_result = {};
for (key in schema) {
value = schema[key];
row_result[key] = _.get(row, value);
}
results.push(row_result);
}
return results;
})();
}});
/* The remainer is just the proof/usage example */
var expected, input, schema;
input = [{
"_type": "MyType",
"_id": "57623535a44b8f1417740a13",
"_source": {
"info": {
"year": 2010,
"number": "string"
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}}];
expected = [{
"_id": "57623535a44b8f1417740a13",
"info": {
"year": 2010,
"number": "string"
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}];
schema = {
"_id": "_id",
"info": "_source.info",
"type": "_source.type",
"date": "_source.date",
"createdBy": "_source.createdBy"
};
console.log('expected result: ' + JSON.stringify(expected, 0, 4));
console.log('actual result: ' + JSON.stringify(_.mapGet(input, schema), 0, 4));
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
Usage:
schema = {
"_id" : "_id",
"info" : "_source.info",
"type" : "_source.type",
"date" : "_source.date",
"createdBy": "_source.createdBy",
}
_.mapGet(input, schema)
Resultant output:
[{
"_id": "57623535a44b8f1417740a13",
"info": {
"year": 2010,
"number": "string"
},
"type": "stolen",
"date": "2016-06-16T00:00:00",
"createdBy": "57469f3c71c8bf2479d225a6"
}]
Note: Complex schema can be more easily described if the source JSON is first converted to a flat, dotted, representation via:
jq [leaf_paths as $path | {"key":$path | join("."), "value":getpath($path) }] |from_entries'

Related

Flattening json with an key exclusion list

I need to flatten the json, but want to consider an exclusion_keys_array list which are not to be processed/added to the list
for example
if I have an exclusion_keys_array = ["addresses.metadata", "pageToken"]
//only metadata of addresses will be skipped (second level skip)
if I have an exclusion_keys_array = ["metadata", "pageToken"]
//metadata of parent json will be skipped (top level key skip)
How do I flatten a JSON using an exclusion array?
Code source: Dynamically generate a 2d array from JSON with varying columns
var exlusion_list = ["metadata", "meta", "pageToken"];
var crowds = [{
"name": [{
"firstName": "John",
"middleName": "Joseph",
"lastName": "Briggs",
}],
"addresses": [{
"type": "home",
"poBox": "111",
"city": "City1",
"postalCode": "1ER001",
"country": "USA",
}, {
"type": "work",
"poBox": "222",
"city": "City2",
"region": "Region2",
"postalCode": "1ER002",
}],
"photos": [{
"url": "photo.org/person1",
"default": true,
}, {
"url": "imagur.org/person1",
"default": true,
}],
"metadata": [{
"meta-id": "1234",
}],
}, {
"name": [{
"firstName": "Bill",
"lastName": "Thatcher",
}],
"addresses": [{
"type": "home",
"city": "City3",
"region": "Region3",
"postalCode": "1ER003",
"country": "USA",
}, {
"type": "work",
"poBox": "444",
"region": "Region4",
"postalCode": "1ER004",
}, {
"poBox": "555",
"region": "Region5",
"postalCode": "1ER005",
}],
"metadata": [{
"meta-id": "1234",
}],
}];
function flatten(obj, res = {}, key = '') {
let add = (d, s) => key ? key + d + s : s;
if (Array.isArray(obj)) {
obj.forEach((v, n) => flatten(v, res, add(' #', n + 1)));
} else if (typeof obj === 'object') {
Object.entries(obj).forEach(([k, v]) => flatten(v, res, add(': ', k)));
} else {
res[key] = obj;
}
return res;
}
let flats = crowds.map(obj => flatten(obj));
function combineKeys(objs) {
let keys = objs.reduce((k, obj) => k.concat(Object.keys(obj)), []);
return [...new Set(keys)];
}
let keys = combineKeys(flats);
let table = flats.map(f => keys.map(k => f[k] ?? ''));
table.unshift(keys);
console.log({ table });
// document.write(JSON.stringify(table));
.as-console-wrapper { min-height: 100%!important; top: 0; }
// .as-console-wrapper { min-height: 70%!important; bottom: 0; }
A quick fix would be filter the keys like below. I think there is a more efficient way to do it but I didn't look into the codes too deep.
let keys = combineKeys(flats).filter(
key => !exlusion_list.includes(key.split(":")[0].split(" ")[0])
);

Slicing an Array and producing an object from it

I have an array and it looks as follow:
[
{
"DT_RowId": "row_4758",
"companies": {
"id": 23,
"email": null,
"name": "test"
},
"USERS": {
"UserId": 23
}
},.....
]
How do I slice it and get only "companies": and the result as follows:
[
{
"id": 23,
"email": null,
"name": "test"
},.....
]
to clear some issues I have added the function in which I'm using data.map
fn.loadData = function (data) {
var dataKeys = Object.keys(data);
console.log(data)// 'data' is an object
console.log(data.map(x => x.companies)) ///data.map not a function error
var infiniteList = document.getElementById('infinite-list');
infiniteList.delegate = {
createItemContent: function (i) {
return ons._util.createElement(
'<ons-list-item modifier="chevron" tappable>' + data[dataKeys[i]].name + '</ons-list-item>'
);
},
countItems: function () {
return Object.keys(data).length;
}
};
infiniteList.refresh();
}
as comments told you to do:
const data = [
{
"DT_RowId": "row_4758",
"companies": {
"id": 23,
"email": null,
"name": "test"
},
"USERS": {
"UserId": 23
}
},
{
"DT_RowId": "row_3758",
"companies": {
"id": 24,
"email": null,
"name": "test3"
},
"USERS": {
"UserId": 24
}
},]
console.log(data.map(obj=>obj.companies))
This worked:
const newArray = [];
for (let i = 0; i < companyArray.length; i++) {
newArray.push(companyArray[i].companies);
}
Thanks, everyone

Javascript test for existence of object key in a object with arrays

I need to check if a property in a complex object (nested objects with arrays) exists or not.
I found several posts on this subject, the most visited the one below.
The problem with the provided solution (checkNested function) doesn't work with objects with arrays.
Does anyone have a solution that cover this case as well?
Cheers.
javascript test for existence of nested object key
This the function I tested:
function checkProperty(obj, prop) {
var parts = prop.split('.');
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
if (obj !== null && typeof obj === "object" && part in obj) {
obj = obj[part];
} else {
return false;
}
}
return true;
}
This is an example of my object:
{
"_msgid": "3ae30deb.af9962",
"topic": "",
"payload": "I am really upset terrible service",
"error": null,
"parts": {
"id": "3ae30deb.af9962",
"type": "array",
"count": 2,
"len": 1,
"index": 0
},
"case_id": "0001",
"features": {
"usage": {
"text_units": 1,
"text_characters": 34,
"features": 7
},
"sentiment": {
"document": {
"score": -0.912124,
"label": "negative"
}
},
"semantic_roles": [{
"subject": {
"text": "I"
},
"sentence": "I am really upset terrible service",
"object": {
"text": "really upset terrible service",
"keywords": [{
"text": "terrible service"
}]
},
"action": {
"verb": {
"text": "be",
"tense": "present"
},
"text": "am",
"normalized": "be"
}
}],
"language": "en",
"keywords": [{
"text": "terrible service",
"sentiment": {
"score": -0.912124
},
"relevance": 0.902721,
"emotion": {
"sadness": 0.462285,
"joy": 0.002207,
"fear": 0.125395,
"disgust": 0.17766,
"anger": 0.575927
}
}],
"entities": [],
"emotion": {
"document": {
"emotion": {
"sadness": 0.462285,
"joy": 0.002207,
"fear": 0.125395,
"disgust": 0.17766,
"anger": 0.575927
}
}
},
"concepts": [],
"categories": [{
"score": 0.99946,
"label": "/health and fitness/disease/headaches and migraines"
}, {
"score": 0.0155692,
"label": "/education/school"
}, {
"score": 0.0141217,
"label": "/family and parenting/children"
}]
}
}
And a failure test:
console.log(checkProperty(msg, 'features.keywords[0].text') ? msg.features.keywords[0].text : "NA");
The checkProperty function you're using doesn't recognize brackets ([ and ]), it only understands dots. So, just give it dots:
checkProperty(msg, 'features.keywords.0.text');

How to check if both the value exists or only one value exists in JavaScript object

I have an Object as below :
{"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [
{
"type": "card",
"accountId": "5299367",
},
{
"type": "Loan",
"accountId": "5299365",
},
{
"type": "card",
"accountId": "8299388",
},
]}
}
What I need to find out if the user has both loan and card or just loan as user product.
Is there any built in function in javascript or angular to find it.
is someone has any suggestion how to do it. Please help.
You could use the filter array method.
var obj = {
"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [
{
"type": "card",
"accountId": "5299367",
},
{
"type": "Loan",
"accountId": "5299365",
},
{
"type": "card",
"accountId": "8299388",
},
]
}
};
var loans = obj.user.products.filter(function(product){
return product.type === "Loan";
});
console.log("Loans: " + loans.length);
// supposing that the user has either a Loan or a card. You could
// easily now find out if the user has only loans as below:
if(loans.length === obj.user.products.length){
console.log("The user has only loans");
}else{
var cards = obj.user.products.length - loans.length;
console.log("The user has "+loans.length+" Loan(s) and "+ cards+ " Card(s).");
}
For further info about this method, please have a look here.
What I need to find out if the user has both loan and card or just
loan as user product.
Based on the above snippet, by using the filter method and comparing the length of the loans with the length of the products, you can answer you question.
You can loop over products and get unique types
var data = {
"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [{
"type": "card",
"accountId": "5299367",
}, {
"type": "Loan",
"accountId": "5299365",
}, {
"type": "card",
"accountId": "8299388",
}, ]
}
}
var result= data.user.products.reduce(function(p,c){
if(p.indexOf(c.type)<0) p.push(c.type)
return p;
}, [])
console.log(result)
You can try following
var obj = {
"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [{
"type": "card",
"accountId": "5299367",
}, {
"type": "Loan",
"accountId": "5299365",
}, {
"type": "card",
"accountId": "8299388",
}, ]
}
};
var results = {};
obj.user.products.reduce(function(oldval, item) {
oldval[item.type] = true;
return oldval;
}, results);
console.log(results.card && results.Loan); // paints true
// Additionally, results have all the information about the unique value for type, hence, can be used as per the need
Same Iteration solution, but immediately gives you if type is different from Loan.
var obj = {"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [
{
"type": "card",
"accountId": "5299367",
},
{
"type": "Loan",
"accountId": "5299365",
},
{
"type": "card",
"accountId": "8299388",
},
]}
};
function haveDifferentProducts(products){
var isDiff = false;
products.forEach(function(product){
isDiff = product.type != "Loan"; //checks if type not loan
});
return isDiff;
}
console.log(haveDifferentProducts(obj.user.products))
Use Array filter() method.
Working demo :
var jsonObj = {
"user": {
"name": "Harry Peter",
"phoneNumber": "12345",
"products": [
{
"type": "card",
"accountId": "5299367",
},
{
"type": "Loan",
"accountId": "5299365",
},
{
"type": "card",
"accountId": "8299388",
},
]
}
};
var totalLoan = jsonObj.user.products.filter(function(item){
return item.type == "Loan";
});
var totalCards = jsonObj.user.products.filter(function(item){
return item.type == "card";
});
if(totalCards.length < 1 && totalLoan.length < 0) {
console.log("Only loan");
} else if (totalCards.length > 0 && totalLoan.length < 1) {
console.log("Only card");
} else {
console.log("Both card as well as loan");
}

Converting javascript object to ordered comma separated value

I am trying to get the items in the json arranged in an orderly manner. I was able to select the "term" values present in the json, but is it possible to arrange this in the manner I have shown in the expected output part? I have added a jsfiddle link to show where I have reached:
[
{
"Link": "http://testLink.com/1",
"_index": "test",
"_source": {
"Author": "SAM",
"Map": [
{
"Company": [
{
"Apple_Inc": [
{
"count": 1,
"term": "Apple"
}
],
"sector": "Technology",
"term": "Apple Inc",
"ticker": "AAPL",
"type": "BCap"
}
],
"count": 1,
"term": "Company"
},
{
"Country": [
{
"Canada": [
{
"Canada": [
{
"count": 1,
"term": "Toronto"
}
],
"count": 1,
"term": "Canada"
}
],
"United_States": [
{
"count": 1,
"term": "United States"
}
],
"currency": "Dollar (USD)",
"index": "DOW JONES INDUS. AVG , S&P 500 INDEX , NASDAQ COMPOSITE INDEX",
"region": "North Americas",
"term": "Canada"
}
],
"count": 1,
"term": "Country"
},
{
"Personality": [
{
"count": 1,
"term": "Bart Prince"
},
{
"count": 1,
"term": "Thomas"
},
{
"count": 1,
"term": "Deborah Hornstra"
},
{
"count": 1,
"term": "Henderson Sotheby"
},
{
"count": 1,
"term": "Max Alliance"
}
],
"count": 5,
"term": "Personality"
}
]
},
"id": "YMFT112"
},
{
"Link": "http://testLink.com/2",
"_id": "YMFT113",
"_index": "test",
"_source": {
"Author": "MAX",
"Map": [
{
"Company": [
{
"Microsoft Corp": [
{
"count": 1,
"term": "Microsoft"
}
],
"sector": "Technology",
"term": "Microsoft",
"ticker": "AAPL",
"type": "BCap"
}
],
"count": 1,
"term": "Company"
},
{
"Country": [
{
"Brazil": [
{
"count": 1,
"term": "Brazil"
}
],
"currency": "Dollar (USD)",
"region": "South Americas",
"term": "Brazil"
}
],
"count": 1,
"term": "Country"
},
{
"SalesRelated": [
{
"count": 1,
"term": "traffic"
}
]
},
{
"Personality": [
{
"count": 1,
"term": "Maximor"
},
{
"count": 1,
"term": "R.V.P"
},
{
"count": 1,
"term": "Wenger"
},
{
"count": 1,
"term": "SAF"
}
],
"count": 4,
"term": "Personality"
}
]
}
}
]
http://jsbin.com/exuwet/3/edit
Prompt Input
If field Selected = Country,
Expected Output:
YMFT112; Country; United States; United States; NA; http://testLink.com/1;
YMFT112; Country; Canada; Canada; Toronto; http://testLink.com/1;
YMFT113; Country; Brazil; Brazil; NA; http://testLink.com/2;
If field Selected = Company,
Expected Output:
YMFT112; Company; Apple Inc; Apple; http://testLink.com/1;
YMFT113; Company; Microsoft Corp; Microsoft; http://testLink.com/2;
You can use the JSON object when natively available or use JSON2 as a shim.
After that it's just a matter of using JavaScript's built in sorting capability. You supply a function that compares to array items against each other
var myArray = JSON.parse(jsonString);
myArray.sort(function(a, b){
var nameA = a._source.Map.Company.term;
var nameB = b._source.Map.Company.term;
if (nameA === nameB) {
return 0;
} else if (nameA < nameB) {
return -1
}
return 1;
});
With eval('(' + json_object + ')'), you will be able to create a JavaScript Object. This object will be an array, and you can acess the properties using ..
For example, if your json_object is called data, for example:
Then
var temp = eval('(' + data + ')'); // temp now is an array.
if you want to access the first _index or id from the json object:
"_index": "test",
"id": "YMFT112",
do alert(temp[0]._index), and it will show you "test". For the other properties, follow the same logic. This stackoverflow question, or the JSON page will help you understand what you have to do in other to have your task accomplished. Yahoo has an API called YUI which may be even more helpful.
Here is a solution using object-scan
// const objectScan = require('object-scan');
const data = [{"_index":"test","id":"YMFT112","_source":{"Author":"SAM","Map":[{"count":1,"term":"Company","Company":[{"sector":"Technology","ticker":"AAPL","Apple_Inc":[{"count":1,"term":"Apple"}],"term":"Apple Inc","type":"BCap"}]},{"count":1,"term":"Country","Country":[{"region":"North Americas","index":"DOW JONES INDUS. AVG , S&P 500 INDEX , NASDAQ COMPOSITE INDEX","United_States":[{"count":1,"term":"United States"}],"term":"Canada","currency":"Dollar (USD)","Canada":[{"count":1,"term":"Canada","Canada":[{"count":1,"term":"Toronto"}]}]}]},{"count":5,"term":"Personality","Personality":[{"count":1,"term":"Bart Prince"},{"count":1,"term":"Thomas"},{"count":1,"term":"Deborah Hornstra"},{"count":1,"term":"Henderson Sotheby"},{"count":1,"term":"Max Alliance"}]}]},"Link":"http://testLink.com/1"},{"_index":"test","_id":"YMFT113","_source":{"Author":"MAX","Map":[{"count":1,"term":"Company","Company":[{"sector":"Technology","ticker":"AAPL","Microsoft Corp":[{"count":1,"term":"Microsoft"}],"term":"Microsoft","type":"BCap"}]},{"count":1,"term":"Country","Country":[{"region":"South Americas","Brazil":[{"count":1,"term":"Brazil"}],"term":"Brazil","currency":"Dollar (USD)"}]},{"SalesRelated":[{"count":1,"term":"traffic"}]},{"count":4,"term":"Personality","Personality":[{"count":1,"term":"Maximor"},{"count":1,"term":"R.V.P"},{"count":1,"term":"Wenger"},{"count":1,"term":"SAF"}]}]},"Link":"http://testLink.com/2"}];
const find = (term, input) => {
const r = objectScan([`[*]._source.Map[*].${term}[*].**.term`], {
reverse: false,
filterFn: ({ key, parents, context }) => {
if (Object.values(parents[0]).some((e) => e instanceof Object)) {
return;
}
const root = parents[parents.length - 2];
context.push([
root.id || root._id,
parents[parents.length - 5].term,
key[key.length - 3].replace(/_/g, ' '),
...parents.slice(0, -7).filter((e) => !Array.isArray(e)).map((p) => p.term).reverse(),
root.Link
]);
}
})(input, []);
const maxLength = Math.max(...r.map((e) => e.length));
r
.filter((e) => e.length < maxLength)
.forEach((e) => e.splice(-1, 0, 'NA'.repeat(maxLength - e.length)));
return r;
};
console.log(find('Country', data).map((e) => e.join('; ')).join('\n'));
/* =>
YMFT112; Country; United States; United States; NA; http://testLink.com/1
YMFT112; Country; Canada; Canada; Toronto; http://testLink.com/1
YMFT113; Country; Brazil; Brazil; NA; http://testLink.com/2
*/
console.log(find('Company', data).map((e) => e.join('; ')).join('\n'));
/* =>
YMFT112; Company; Apple Inc; Apple; http://testLink.com/1
YMFT113; Company; Microsoft Corp; Microsoft; http://testLink.com/2
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Categories