I want to map a $scope.filters object to a var criteria on the condition if the original fields are null or not.
So lets say I have:
$scope.filters = {
name: 'myName'
lastName: null,
age: null,
}
I want my var criteria to be mapped to non null fields like this:
var criteria = {
name: 'myName';
}
So I have tried like this:
var criteria = {};
angular.forEach($scope.filters, function (value, key, obj) {
if (value != null) {
this.push(obj)
}
}, criteria);
But I guess I'm missing something.
criteria is an object, not an array, so you can't push it. Either make it an array if you want to store separate individual criteria, or replace the push with this[key] = value;
You should do it like this,
$scope.filters = {
name: 'myName'
lastName: null,
age: null,
}
var criteria = []; //array, you were using object
angular.forEach($scope.filters, function(value, key) {
if (value != null) {
this.push(key + ': ' + value);
}
}, criteria);
expect(criteria).toEqual(['name: myName']);
Hope it helps.
Related
What is the best way to filter out data that exists within an object?
I was able to do use the below code when data was just an array of values but now I need to filter out any data where the item.QID exists in my array of objects.
Data Obj:
var data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob
}]
Snippet:
// I don't want to include data if this QID is in my object
this.employees = emp.filter(item =>!this.data.includes(item.QID));
From what I understand, includes only works on an array so I need to treat all of the QID values in my object as an array.
Desired Outcome: (assuming item.QID = ABC123)
this.employees = emp.filter(item =>!this.data.includes('ABC123'));
Result:
var data = [{
QID: 'DEF456',
Name: 'Bob'
}]
UPDATE:
Apologies, I left some things a little unclear trying to only include the necessary stuff.
// People Search
this.peopleSearchSub = this.typeahead
.distinctUntilChanged()
.debounceTime(200)
.switchMap(term => this._mapsService.loadEmployees(term))
.subscribe(emp => {
// Exclude all of the current owners
this.employees = emp.filter((item) => item.QID !== this.data.QID);
}, (err) => {
this.employees = [];
});
The above code is what I am working with. data is an object of users I want to exclude from my type-ahead results by filtering them out.
The question is a little ambiguous, but my understanding (correct me if I'm wrong), is that you want to remove all items from a list emp that have the same QID as any item in another list data?
If that's the case, try:
this.employees = emp.filter(item => !this.data.some(d => d.QID === item.QID))
some is an array method that returns true if it's callback is true for any of the arrays elements. So in this case, some(d => d.QID === item.QID) would be true if ANY of the elements of the list data have the same QID as item.
Try Object#hasOwnProperty()
this.employees = emp.filter(item =>item.hasOwnProperty('QID'));
You can use a for ... in to loop through and filter out what you want:
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
let newData = [];
let filterValue = 'ABC123';
for (let value in data) {
if (data[value].QID !== filterValue) {
newData.push(data[value]);
}
}
newData will be your new filtered array in this case
You can use an es6 .filter for that. I also added a couple of elements showing the filtered list and an input to allow changing of the filtered value. This list will update on the click of the button.
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
displayData(data);
function displayData(arr) {
let str = '';
document.getElementById('filterList').innerHTML = '';
arr.forEach((i) => { str += "<li>" + i.QID + ": " + i.Name + "</li>"})
document.getElementById('filterList').innerHTML = str;
}
function filterData() {
let filterValue = document.getElementById('filterInput').value;
filterText (filterValue);
}
function filterText (filterValue) {
let newArr = data.filter((n) => n.QID !== filterValue);
displayData(newArr)
}
<input id="filterInput" type="text" value="ABC123" />
<button type ="button" onclick="filterData()">Filter</button>
<hr/>
<ul id="filterList"><ul>
A React component is passed a state property, which is an object of objects:
{
things: {
1: {
name: 'fridge',
attributes: []
},
2: {
name: 'ashtray',
attributes: []
}
}
}
It is also passed (as a router parameter) a name. I want the component to find the matching object in the things object by comparing name values.
To do this I use the filter method:
Object.keys(this.props.things).filter((id) => {
if (this.props.things[id].name === this.props.match.params.name) console.log('found!');
return (this.props.things[id].name === this.props.match.params.name);
});
However this returns undefined. I know the condition works because of my test line (the console.log line), which logs found to the console. Why does the filter method return undefined?
Object.keys returns an array of keys (like maybe ["2"] in your case).
If you are interested in retrieving the matching object, then you really need Object.values. And if you are expecting one result, and not an array of them, then use find instead of filter:
Object.values(this.props.things).find((obj) => {
if (obj.name === this.props.match.params.name) console.log('found!');
return (obj.name === this.props.match.params.name);
});
Be sure to return that result if you use it within a function. Here is a snippet based on the fiddle you provided in comments:
var state = {
things: {
1: {
name: 'fridge',
attributes: []
},
2: {
name: 'ashtray',
attributes: []
}
}
};
var findThing = function(name) {
return Object.values(state.things).find((obj) => {
if (obj.name === name) console.log('found!');
return obj.name === name;
});
}
var result = findThing('fridge');
console.log(result);
You need to assign the result of filter to a object and you get the result as the [id]. You then need to get the object as this.props.things[id]
var data = {
things: {
1: {
name: 'fridge',
attributes: []
},
2: {
name: 'ashtray',
attributes: []
}
}
}
var name = 'fridge';
var newD = Object.keys(data.things).filter((id) => {
if (data.things[id].name === name) console.log('found!');
return (data.things[id].name === name);
});
console.log(data.things[newD]);
I am using api which return me this response:-
{
VJiQND:
{
optin: 'double',
is_default: 'no',
from_email: 'xxxxxxx#gmail.com',
name: 'ding',
reply_to_email: 'xxxxxxx#gmail.com',
created_on: '2015-05-18 11:32:37',
from_name: 'Xa'
},
VJj3KAS:
{
optin: 'double',
is_default: 'yes',
from_email: 'xxxx#gmail.com',
name: 'xtusxx',
reply_to_email: 'xxxxx#gmail.com',
created_on: '2015-05-18 09:09:43',
from_name: 'Ads'
}
}
now I want to get VJj3KAS if from_name equals to 'Ads' I try filter function but it didn't work it throw me an error can you please help me to get that value.
Thanks
The filter function works for arrays and what you have there is a map. You must simply iterate through the map key/value pairs and check if they match your condition.
function getFilteredKeys(map, filter) {
var filteredKeys = [];
for (var key in map) {
if (filter(key, map[key])) {
filteredKeys.push(key);
}
}
return filteredKeys;
}
Usage:
var keys = getFilteredKeys(apiResult, function(key, value){
return value.from_name && value.from_name === 'Ads';
});
I have an array of objects that can be of any length and any depth. I need to be able to find an object by its id and then modify that object within the array. Is there an efficient way to do this with either lodash or pure js?
I thought I could create an array of indexes that led to the object but constructing the expression to access the object with these indexes seems overly complex / unnecessary
edit1; thanks for all yours replies I will try and be more specific. i am currently finding the location of the object I am trying to modify like so. parents is an array of ids for each parent the target object has. ancestors might be a better name for this array. costCenters is the array of objects that contains the object I want to modify. this function recurses and returns an array of indexes that lead to the object I want to modify
var findAncestorsIdxs = function(parents, costCenters, startingIdx, parentsIdxs) {
var idx = startingIdx ? startingIdx : 0;
var pidx = parentsIdxs ? parentsIdxs : [];
_.each(costCenters, function(cc, ccIdx) {
if(cc.id === parents[idx]) {
console.log(pidx);
idx = idx + 1;
pidx.push(ccIdx);
console.log(pidx);
pidx = findAncestorsIdx(parents, costCenters[ccIdx].children, idx, pidx);
}
});
return pidx;
};
Now with this array of indexes how do I target and modify the exact object I want? I have tried this where ancestors is the array of indexes, costCenters is the array with the object to be modified and parent is the new value to be assigned to the target object
var setParentThroughAncestors = function(ancestors, costCenters, parent) {
var ccs = costCenters;
var depth = ancestors.length;
var ancestor = costCenters[ancestors[0]];
for(i = 1; i < depth; i++) {
ancestor = ancestor.children[ancestors[i]];
}
ancestor = parent;
console.log(ccs);
return ccs;
};
this is obviously just returning the unmodified costCenters array so the only other way I can see to target that object is to construct the expression like myObjects[idx1].children[2].grandchildren[3].ggranchildren[4].something = newValue. is that the only way? if so what is the best way to do that?
You can use JSON.stringify for this. It provides a callback for each visited key/value pair (at any depth), with the ability to skip or replace.
The function below returns a function which searches for objects with the specified ID and invokes the specified transform callback on them:
function scan(id, transform) {
return function(obj) {
return JSON.parse(JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null && value.id === id) {
return transform(value);
} else {
return value;
}
}));
}
If as the problem is stated, you have an array of objects, and a parallel array of ids in each object whose containing objects are to be modified, and an array of transformation functions, then it's just a matter of wrapping the above as
for (i = 0; i < objects.length; i++) {
scan(ids[i], transforms[i])(objects[i]);
}
Due to restrictions on JSON.stringify, this approach will fail if there are circular references in the object, and omit functions, regexps, and symbol-keyed properties if you care.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter for more info.
As Felix Kling said, you can iterate recursively over all objects.
// Overly-complex array
var myArray = {
keyOne: {},
keyTwo: {
myId: {a: '3'}
}
};
var searchId = 'myId', // Your search key
foundValue, // Populated with the searched object
found = false; // Internal flag for iterate()
// Recursive function searching through array
function iterate(haystack) {
if (typeof haystack !== 'object' || haystack === null) return; // type-safety
if (typeof haystack[searchId] !== 'undefined') {
found = true;
foundValue = haystack[searchId];
return;
} else {
for (var i in haystack) {
// avoid circular reference infinite loop & skip inherited properties
if (haystack===haystack[i] || !haystack.hasOwnProperty(i)) continue;
iterate(haystack[i]);
if (found === true) return;
}
}
}
// USAGE / RESULT
iterate(myArray);
console.log(foundValue); // {a: '3'}
foundValue.b = 4; // Updating foundValue also updates myArray
console.log(myArray.keyTwo.myId); // {a: '3', b: 4}
All JS object assignations are passed as reference in JS. See this for a complete tutorial on objects :)
Edit: Thanks #torazaburo for suggestions for a better code.
If each object has property with the same name that stores other nested objects, you can use: https://github.com/dominik791/obj-traverse
findAndModifyFirst() method should solve your problem. The first parameter is a root object, not array, so you should create it at first:
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
Then use findAndModifyFirst() method:
findAndModifyFirst(rootObj, 'children', { id: 1 }, replacementObject)
replacementObject is whatever object that should replace the object that has id equal to 1.
You can try it using demo app:
https://dominik791.github.io/obj-traverse-demo/
Here's an example that extensively uses lodash. It enables you to transform a deeply nested value based on its key or its value.
const _ = require("lodash")
const flattenKeys = (obj, path = []) => (!_.isObject(obj) ? { [path.join('.')]: obj } : _.reduce(obj, (cum, next, key) => _.merge(cum, flattenKeys(next, [...path, key])), {}));
const registrations = [{
key: "123",
responses:
{
category: 'first',
},
}]
function jsonTransform (json, conditionFn, modifyFn) {
// transform { responses: { category: 'first' } } to { 'responses.category': 'first' }
const flattenedKeys = Object.keys(flattenKeys(json));
// Easily iterate over the flat json
for(let i = 0; i < flattenedKeys.length; i++) {
const key = flattenedKeys[i];
const value = _.get(json, key)
// Did the condition match the one we passed?
if(conditionFn(key, value)) {
// Replace the value to the new one
_.set(json, key, modifyFn(key, value))
}
}
return json
}
// Let's transform all 'first' values to 'FIRST'
const modifiedCategory = jsonTransform(registrations, (key, value) => value === "first", (key, value) => value = value.toUpperCase())
console.log('modifiedCategory --', modifiedCategory)
// Outputs: modifiedCategory -- [ { key: '123', responses: { category: 'FIRST' } } ]
I needed to modify deeply nested objects too, and found no acceptable tool for that purpose. Then I've made this and pushed it to npm.
https://www.npmjs.com/package/find-and
This small [TypeScript-friendly] lib can help with modifying nested objects in a lodash manner. E.g.,
var findAnd = require("find-and");
const data = {
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Three',
},
],
};
findAnd.changeProps(data, { id: 2 }, { name: 'Foo' });
outputs
{
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Foo',
},
],
}
https://runkit.com/embed/bn2hpyfex60e
Hope this could help someone else.
I wrote this code recently to do exactly this, as my backend is rails and wants keys like:
first_name
and my front end is react, so keys are like:
firstName
And these keys are almost always deeply nested:
user: {
firstName: "Bob",
lastName: "Smith",
email: "bob#email.com"
}
Becomes:
user: {
first_name: "Bob",
last_name: "Smith",
email: "bob#email.com"
}
Here is the code
function snakeCase(camelCase) {
return camelCase.replace(/([A-Z])/g, "_$1").toLowerCase()
}
export function snakeCasedObj(obj) {
return Object.keys(obj).reduce(
(acc, key) => ({
...acc,
[snakeCase(key)]: typeof obj[key] === "object" ? snakeCasedObj(obj[key]) : obj[key],
}), {},
);
}
Feel free to change the transform to whatever makes sense for you!
I have an array of cars like this:
[{ name:"Toyota Minivan", id:"506" }, { name:"Honda Civic", id:"619" }]
I am trying to check whether the array contains a certain id.
I have tried
var x =!!_.where(cars, {id:'506'}).length;
expecting it to return true if the array contains the id, but it always returns false.
What am I doing here ?
Btw, I don't have to use underscore.js if there is a better way of doing this.
thanks
Thomas
Your code does work (once you fix the syntax errors in the object array):
http://jsfiddle.net/ArPCa/
var cars = [{ name:"Toyota Minivan", id:"506"}, { name:"Honda Civic", id:"619"}];
var x =!!_.where(cars, {id:'506'}).length;
console.log('value: ' + x);
returns "value: true". So there must be a problem somewhere else.
But, a better way to do this might be some:
var y = _.some(cars, function(c) {
return c.id == '506';
});
I know this is late, but even easier:
var cars = [{ name:"Toyota Minivan", id:"506"}, { name:"Honda Civic", id:"619"}];
function findById(id) {
return _.contains(_.pluck(cars, 'id'), id);
}
Say you have the array arr, and your id, id.
arr.filter(function(elem) { return elem.id == id; });
will return an array with the matching element(s);
You could wrap it in a function like:
function findById(arr, id) {
var filtered = arr.filter(function(elem) { return elem.id == id; });
return filtered && filtered.length ? filtered[0] : null;
}
, potentially doing some other stuff if you weren't happy with the default filtered array.
var obj = [{
name: "Toyota Minivan",
id: "506"
}, {
name: "Honda Civic",
id: "619"
}];
function findCar(id) {
return obj.filter(function (car) {
if (car.id == id) {
return car;
}
});
}