how to grep this Javascript object array? - javascript

I have the following object structure:
var mapData =
{
Summary:
{
ReportName: 'Month End Report'
},
NortheastRegion:
{
Property1: 123,
RegionName: 'Northeast'
},
SoutheastRegion:
{
Property1: 456,
RegionName: 'Southeast'
},
}
I want to write a grep function that returns an array of region names. The following function is not returning any values:
var regions = $.grep(mapData, function(n,i)
{
return n.RegionName;
});
What am I missing here?

$.grep is for filtering arrays. Your structure isn't an array. $.grep is also just for filtering, but you're talking about both filtering (leaving out Summary) and mapping (getting just the region names).
Instead, you can use
Object.keys and push:
var regions = [];
Object.keys(mapData).forEach(function(key) {
var entry = mapData[key];
if (entry && entry.RegionName) {
regions.push(entry.RegionName);
}
});
Object.keys, filter, and map:
var regions = Object.keys(mapData)
.filter(function(key) {
return !!mapData[key].RegionName;
})
.map(function(key) {
return mapData[key].RegionName;
});
A for-in loop and push:
var regions = [];
for (var key in mapData) {
if (mapData.hasOwnProperty(key)) {
var entry = mapData[key];
if (entry && entry.RegionName) {
regions.push(entry.RegionName);
}
}
}
...probably others.

That's an object, not an array. According to the jQuery docs, your above example would work if mapData were an array.
You can use lodash's mapValues for this type of thing:
var regions = _.mapValues(mapData, function(o) {
return o.RegionName;
});
ES6:
const regions = _.mapValues(mapData, o => o.RegionName)

As stated in jQuery.grep() docs, you should pass an array as data to be searched, but mapData is an object. However, you can loop through the object keys with Object.keys(), but AFAIK you'll have to use function specific for your case, like:
var mapData =
{
Summary:
{
ReportName: 'Month End Report'
},
NortheastRegion:
{
Property1: 123,
RegionName: 'Northeast'
},
SoutheastRegion:
{
Property1: 456,
RegionName: 'Southeast'
},
};
var keys = Object.keys(mapData),
result = [];
console.log(keys);
keys.forEach(function(key) {
var region = mapData[key].RegionName;
if (region && result.indexOf(region) == -1) {
result.push(region);
}
});
console.log(result);
// Short version - based on #KooiInc answer
console.log(
Object.keys(mapData).map(m => mapData[m].RegionName).filter(m => m)
);

$.grep is used for arrays. mapData is an object. You could try using map/filter for the keys of mapData, something like:
var mapData =
{
Summary:
{
ReportName: 'Month End Report'
},
NortheastRegion:
{
Property1: 123,
RegionName: 'Northeast'
},
SoutheastRegion:
{
Property1: 456,
RegionName: 'Southeast'
},
};
var regionNames = Object.keys(mapData)
.map( function (key) { return mapData[key].RegionName; } )
.filter( function (name) { return name; } );
console.dir(regionNames);
// es2105
var regionNames2 = Object.keys(mapData)
.map( key => mapData[key].RegionName )
.filter( name => name );
console.dir(regionNames2);

Just turn $.grep to $.map and you would good to go.
var regions = $.map(mapData, function(n,i)
{
return n.RegionName;
});

Related

JavaScript - iterate through object and change nested properties

This is an object to be processed:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
I would like to go through each key of q and if the corresponding value is an object that has the property contains then replace it with $regex.
Related information can be found here: JavaScript: Object Rename Key
You can try the following way:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
for(var k in q){
if(q[k].hasOwnProperty('contains')){
Object.defineProperty(q[k], '$regex',
Object.getOwnPropertyDescriptor(q[k], 'contains'));
delete q[k]['contains'];
}
}
console.log(q);
for(const obj of Object.values(q)) {
obj.$regex = obj.contains;
delete obj.contains;
}
Just go over all values inside q and copy the contains property into the $regex property.
To iterate over object keys first you have to fetch them, here is one simple approach
const keys = Object.keys(q); // ["email", "name"]
Now iterate over the array which we got and perform regex testing;
keys.forEach(key => {
let value = q[key].contains;
// create $regex and assign value
// remove .contains
})
You can loop through the objects and first put current value of contains property in $regex and then delete the contains property.
Below is working code:
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
}
};
for (var i of Object.values(q)) {
if (i.hasOwnProperty("contains")) {
i.$regex = i.contains;
delete i.contains;
}
}
console.log(q);
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
Object.keys(q).forEach(k => {
if (typeof q[k].contains != 'undefined'){
q[k].$regex = q[k].contains;
delete q[k].contains;
}
})
console.log(q);
Other version using Es 6 features
const renameProp = (
oldProp,
newProp,
{ [oldProp]: old, ...others }
) => {
return {
[newProp]: old,
...others
};
};
let q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
let newObj = {}
for (let propName in q) {
newObj[propName] = renameProp("contains","$regex",q[propName])
}
console.log(newObj)
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
},
asdf: "asdf"
};
Object.keys(q).forEach(function(item, index) {
if (typeof q[item] == "object" && q[item].contains) {
q[item].$regex = q[item].contains;
delete q[item].contains;
}
})

How to split a doted string and retrive the data from object by notation?

At present, I do this approach:
var obj = {
sender: {
name: "tech"
}
}
var str = "sender.name".split('.');
console.log( obj[str[0]][str[1]] ); //getting update as 'Tech'
In the above I use obj[str[0]][str[1]] for just 2 step, this is works fine. In case if I received a long node parent and child this approach not going to work.
Instead is there any correct dynamic way to do this?
You can use array#reduce to navigate through each key.
var obj = { sender: { name: "tech" } };
var str = "sender.name".split('.').reduce((r,k) => r[k],obj);
console.log(str);
You can use reduce:
var obj = {
foo: {
bar: {
baz: {
sender: {
name: "tech"
}
}
}
}
}
const props = "foo.bar.baz.sender.name".split('.');
const val = props.reduce((currObj, prop) => currObj[prop], obj);
console.log(val);
You could split the string and reduce the path for the result. The function uses a default object for missing or not given properties.
function getValue(object, path) {
return path
.split('.')
.reduce(function (o, k) { return (o || {})[k]; }, object);
}
var obj = { sender: { name: "tech" } },
str = "sender.name";
console.log(getValue(obj, str));
You should be looking into libraries such as "https://lodash.com/"
https://lodash.com/docs/4.17.10
Use _.get : https://lodash.com/docs/4.17.10#get
You can simply write _.get(obj, 'sender.name', 'default') and you will get the value as you expect

Javascript forEach where value equals value in different array

Javascript:
$(document).ready(function() {
const ores = "../js/json/oreList.json";
const priceURL = "https://esi.tech.ccp.is/latest/markets/prices/?datasource=tranquility";
let oreArray = [];
let priceArray = [];
let total = 0;
// Retrieve list of ores
function getOres() {
$.getJSON(ores, function(ores) {
ores.forEach(function(ore) {
total++;
if (total === 48) {
getPrices();
}
oreArray.push(ore);
});
});
}
// Retrieve all items & prices via API
function getPrices() {
$.getJSON(priceURL, function(prices) {
prices.forEach(function(data) {
priceArray.push(data);
console.log(data);
});
});
}
getOres();
});
The first function creates an internal array from my .JSON file and the second function creates an internal array from the URL.
In the first array oreArray, an object looks like this:
{ id: 1234, name: "Title" }
In the second array priceArray, an object looks like this:
{ type_id: 1234, average_price: 56.34 }
My oreArray has 48 objects and unfortunately the priceArray has about 11,000 objects. I need to create a new array by comparing the two arrays and building new objects, where the ID's match. So for example objects in newArray would look like:
{ id: 1234, name: "Title", average_price: 56.34 }
Basically I'm having trouble figuring out the logic for:
For each object in oreArray, find the object with the same ID value in priceArray and append the new array with a new object using values from both arrays.
I would do it this way:
const ores = "../js/json/oreList.json",
priceURL = "https://esi.tech.ccp.is/latest/markets/prices/?datasource=tranquility";
let oreArray,
priceArray,
joinedArray = [];
function getOres() {
$.getJSON(ores, function(ores) {
oreArray = ores;
getPrices();
});
}
function getPrices() {
$.getJSON(priceURL, function(prices) {
priceArray = prices;
joinPrices();
});
}
function joinPrices() {
oreArray.forEach(function(ore) {
var matchingPrice = getMatchingPrice(ore);
if(matchingPrice !== false) {
joinedArray.push({
id: ore.id,
name: ore.name,
average_price: matchingPrice.average_price
});
}
});
}
function getMatchingPrice(ore) {
for(var i=0; i<priceArray.length; i++) {
if(priceArray[i].type_id === ore.id) {
return priceArray[i];
}
}
return false;
}
getOres();
I think that a good way to approach this problem is by changing the data structure of the average prices a little bit.
Instead of having them in an array, where each item has type_id and average_price field, you might want to consider using an object to store them, where the key is the type_id and the value is the average_price.
To be more concrete, you can replace:
prices.forEach(function(data) {
priceArray.push(data);
});
With:
const pricesMap = {};
prices.forEach(price => {
pricesMap[price.type_id] = price.average_price
});
And when looping on the oreArray, you can access each product's average_price by simply referring to pricesMap[ore.id]
You can check out this JSBin: http://jsbin.com/fogayaqexe/edit?js,console
You can use reduce to loop over each oreArr item and collect the data you need in the accumulator:
var oreArr=[
{ id: 1234, name: "Title" },
{ id: 2234, name: "2Title" },
]
var priceArr= [
{ type_id: 1234, average_price: 56.34 },
{ type_id: 2234, average_price: 256.34 },
{ type_id: 3234, average_price: 56.34 },
{ type_id: 4234, average_price: 56.34 },
]
var resArr = oreArr.reduce((ac,x) => {
var priceMatch = priceArr.find( z => z.type_id === x.id )
if(! priceMatch)
return ac //bail out if no priceMatch found
var res = Object.assign({}, x, priceMatch)
ac.push(res)
return ac
},[])
console.log(resArr)
other methods used:
arrayFind to check intersection
Object.assign to create the merged object to populate the accumulator
I suggest you to change your small json as object
eg : '{"1234":{"id": 1234, "name": "Title" }}';
var json = '{"1234":{"id": 1234, "name": "Title" }}';
oreArray = JSON.parse(json);
alert(oreArray['1234'].name); // oreArray[priceArraySingle.id].name
we can easily match priceArray id with oreArray.

Alternative for _.invert() using underscore library

I need to change the existing map swapping keys into values and values into keys. As there is duplicate values in my map for the keys I cannot use _.invert() of underscore library.
function map() {
return {
'eatables': {
apple: 'fruits',
orange: 'fruits',
guava: 'fruits',
brinjal: 'vegetables',
beans: 'vegetables',
rose: 'flowers',
}
}
}
var reverseMap = _.invert(map()['eatables']);
// invert function works for distinct values.
console.log (reverseMap);
// which is giving Object {fruits: "guava", vegetables: "brinjal",flowers:"rose"}
But i am expecting an output as
Object {fruits: ["apple","orange","guava"], vegetables: ["brinjal","beans"], flowers:"rose"}
I tried as below, i just stuck how to find whether map value is distinct or multiple?
var newObj = invert(map()['eatables']);
_.each(newObj, function(key) {
if (Array.isArray(key)) {
_.each( key, function(value) {
console.log(value);
});
} else {
console.log("else:"+key);
}
});
function invert(srcObj) {
var newObj = {};
_.groupBy(srcObj, function(value, key ) {
if (!newObj[value]) newObj[value] = []; //Here every thing is array, can i make it string for values which are unique.
newObj[value].push(key);
});
return newObj;
}
Let me any alternative using underscore library.
You can use this function. This function uses Object.keys to generate an array containing the keys of the object passed in input. Then, it accesses the values of the original object and use them as key in the new object. When two values map to the same key, it pushes them into an array.
function invert(obj) {
var result = {};
var keys = Object.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (result[obj[keys[i]]] instanceof Array) {
result[obj[keys[i]]].push(keys[i])
} else if (result[obj[keys[i]]]) {
var temp = result[obj[keys[i]]];
result[obj[keys[i]]] = [temp, keys[i]];
} else {
result[obj[keys[i]]]=keys[i];
}
}
return result;
}
https://jsfiddle.net/6f2ptxgg/1/
You can use the underscore each to iterate through your data and push the result in an array. It should give you your expected output.
function customInvert(data) {
var result = {};
_.each(data, function (value, key) {
if (_.isUndefined(result[value])) {
result[value] = key;
} else if(_.isString(result[value])) {
result[value] = [result[value], key];
} else {
result[value].push(key)
}
});
return result;
}
customInvert({
apple: 'fruits',
orange: 'fruits',
guava: 'fruits',
brinjal: 'vegetables',
beans: 'vegetables',
rose: 'flowers',
})

Lodash sort collection based on external array

I have an array with keys like so:
['asdf12','39342aa','12399','129asg',...]
and a collection which has these keys in each object like so:
[{guid: '39342aa', name: 'John'},{guid: '129asg', name: 'Mary'}, ... ]
Is there a fast way to sort the collection based on the order of keys in the first array?
var sortedCollection = _.sortBy(collection, function(item){
return firstArray.indexOf(item.guid)
});
Here is just a simple add to the accepted answer in case you want to put the unmatched elements at the end of the sortedCollection and not at the beginning:
const last = collection.length;
var sortedCollection = _.sortBy(collection, function(item) {
return firstArray.indexOf(item.guid) !== -1? firstArray.indexOf(item.guid) : last;
});
Input:
var data1 = ['129asg', '39342aa'];
var data2 = [{
guid: '39342aa',
name: 'John'
}, {
guid: '129asg',
name: 'Mary'
}];
First create an index object, with _.reduce, like this
var indexObject = _.reduce(data2, function(result, currentObject) {
result[currentObject.guid] = currentObject;
return result;
}, {});
And then map the items of the first array with the objects from the indexObject, like this
console.log(_.map(data1, function(currentGUID) {
return indexObject[currentGUID]
}));
Output
[ { guid: '129asg', name: 'Mary' },
{ guid: '39342aa', name: 'John' } ]
Note: This method will be very efficient if you want to sort so many objects, because it will reduce the linear look-up in the second array which would make the entire logic run in O(M * N) time complexity.
This is the efficient & clean way:
(Import lodash identity and sortBy):
TS:
function sortByArray<T, U>({ source, by, sourceTransformer = identity }: { source: T[]; by: U[]; sourceTransformer?: (item: T) => U }) {
const indexesByElements = new Map(by.map((item, idx) => [item, idx]));
const orderedResult = sortBy(source, (p) => indexesByElements.get(sourceTransformer(p)));
return orderedResult;
}
Or in JS:
function sortByArray({ source, by, sourceTransformer = _.identity }) {
const indexesByElements = new Map(by.map((item, idx) => [item, idx]));
const orderedResult = _.sortBy(source, (p) => indexesByElements.get(sourceTransformer(p)));
return orderedResult;
}
You can use indexBy(), and at() to sort your collection. The advantage being that concise code and performance. Using sortBy() here does the trick, but your external array is already sorted:
var ids = [ 'cbdbac14', 'cf3526e2', '189af064' ];
var collection = [
{ guid: '189af064', name: 'John' },
{ guid: 'cf3526e2', name: 'Julie' },
{ guid: 'cbdbac14', name: 'James' }
];
_(collection)
.indexBy('guid')
.at(ids)
.pluck('name')
.value();
// → [ 'James', 'Julie', 'John' ]
Using at(), you can iterate over the sorted external collection, building a new collection from the source collection. The source collection has been transformed into an object using indexBy(). You do this so at() has key-based access for each of it's ids.

Categories