What would be the best way to remove any additional properties from an object that is not defined in defaults object?
var
defaults = {
color : 'blue',
size: 9,
price : 40.00,
instock : true
},
newItem = {
color: 'red',
size : 4,
price : 20.00
extra : invalid // discard this
extra1 : invalid // discard this
},
item = $.extend( defaults, newObject ) ;
Desired output....
{
color : 'red',
size: 4,
price : 20.00,
instock : true
}
You could reduce Object.keys(defaults) to an object containing either the override value or the default value:
var defaults = {
color : 'blue',
size: 9,
price : 40.00,
instock : true
},
newItem = {
color: 'red',
size : 4,
price : 20.00,
extra : 'invalid',
extra1 : 'invalid'
};
function getOverrides(defaults, obj) {
return Object.keys(defaults).reduce(function(result, cur) {
result[cur] = cur in obj ? obj[cur] : defaults[cur];
return result;
}, {});
}
console.log(getOverrides(defaults, newItem));
According to this performance comparison:
https://jsperf.com/dictionary-contains-key
The most efficient way to do this is:
for(attr in newItem) {
if(defaults[attr] === undefined)
delete newItem[attr];
}
Before you call $.extend, put the following.
for(variable in newItem) {
if(!(variable in defaults)) {
delete newItem[variable];
}
}
This will loop over every key in newItem and check that it is also a key in defaults. Note that this will modify newItem, so if that is not desired, you'll need to do some tweaking.
Only merge properties which existing in defaults object: (simple and supports old browsers)
var defaults = {
color : 'blue',
size: 9,
price : 40.00,
instock : true
};
var newItem = {
color: 'red',
size : 4,
price : 20.00,
extra : 'invalid', // discard this
extra1 : 'invalid' // discard this
};
var result = {};
for (var i in defaults) {
result[i] = newItem.hasOwnProperty(i) ? newItem[i] : defaults[i];
}
console.log(result);
Some code that I have been playing with that may be of interest, and an example of how to use it with your question.
'use strict';
var slice = Function.call.bind(Array.prototype.slice);
var reflectAssign = function assign(target) {
return slice(arguments, 1).every(function(source) {
if (source == null) {
return true;
}
var object = Object(source);
return Reflect.ownKeys(object).every(function(key) {
return Reflect.set(target, key, object[key]);
});
});
};
var reflectAssignHas = function(target) {
var targetKeys = Reflect.ownKeys(target);
return slice(arguments, 1).every(function(source) {
if (source == null) {
return true;
}
var object = Object(source);
return targetKeys.every(function(key) {
return Reflect.has(object, key) ? Reflect.set(target, key, object[key]) : true
});
});
};
var defaults = {
color: 'blue',
size: 9,
price: 40.00,
instock: true
};
var newItem = {
color: 'red',
size: 4,
price: 20.00,
extra: 'invalid', // discard this
extra1: 'invalid' // discard this
};
var item = {};
console.log(reflectAssign(item, defaults));
console.log(reflectAssignHas(item, newItem));
console.log(item);
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-sham.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.3/es6-shim.js"></script>
Related
I have Object List Data.
I have to sort wise uniqueid by value. and their data store respective uniqueid Object.
** Object List **
{
uniqueid : 200,
name : "sandesh",
loop: 222,
salary : 2500
},
{
uniqueid : 300,
name : "hello",
loop: 222,
salary : 2500
},
{
uniqueid : 300,
name : "hello1",
loop: 222,
salary : 2500
};
I will create an object uniqueid wise and Store there value but every data create duplicate object.
var newArray = [];
this.userBetHistory.forEach(item => {
var newItem = {uniqueid : item.uniqueid, BetData: []};
this.userBetHistory.forEach(innerItem => {
if(innerItem.uniqueid== item.uniqueid){
newItem.BetData = newItem.BetData.concat(innerItem);
}
});
newArray.push(newItem);
});
** Expected Output: **
{
uniqueid : 200,
data :
{
uniqueid : 200,
name : "sandesh",
loop: 222,
salary : 2500
}
},
{
uniqueid : 300,
data :
{
uniqueid : 300,
name : "hello",
loop: 222,
salary : 2500
},
{
uniqueid : 300,
name : "hello1",
loop: 222,
salary : 2500
}
}
You can use Array.reduce for formatting the data
let data = [{uniqueid:200,name:'sandesh',loop:222,salary:2500,},{uniqueid:300,name:'hello',loop:222,salary:2500,},{uniqueid:300,name:'hello1',loop:222,salary:2500,},]
const formattedData = (data) => {
const result = data.reduce((res, d) => {
if(res[d.uniqueid]) {
res[d.uniqueid].data.push(d);
} else {
res[d.uniqueid] = {
uniqueid: d.uniqueid,
data: [
{...d}
]
}
}
return res;
}, {}) ;
return Object.values(result);
}
console.log(formattedData(data))
Hope this is what the output that you are looking for
You can do it with some mapping and filtering.
const userBetHistory = [{uniqueid: 200,name: "sandesh",loop: 222,salary: 2500,},{uniqueid: 300,name: "hello",loop: 222,salary: 2500,},{uniqueid: 300,name: "hello1",loop: 222,salary: 2500, }];
let tempData = userBetHistory.map((activeBet) => {
const activeBetSubData = userBetHistory.filter(
(bet) => bet.uniqueid === activeBet.uniqueid
);
return {
uniqueid: activeBetSubData[0].uniqueid,
data: activeBetSubData,
};
});
tempData = tempData
.map((item) => item.uniqueid)
.map((item, i, final) => final.indexOf(item) === i && i)
.filter((item) => tempData[item])
.map((item) => tempData[item]);
console.log(tempData);
I have 2 object arrays of different types with different key combinations.
How to perform efficient matching and object update for below code? I have tried different combination with .filter but no success yet.
I am updating the temporary masterList based on selectionList.
for (let selObj of selctionList) {
for (let obj of masterList) {
if (selObj['n'] == obj['name']) {
obj['checked'] = true;
obj['cost'] = selObj['r'];
obj['qty'] = (selObj['q'] ? selObj['q'] : 1);
break;
}
}
}
for (let obj of masterList) {
if (!obj['checked']) {
obj['checked'] = false;
}
}
Sample Data
`masterList =
[{"id":459,"cost":250,"name":"Coke"},
{"id":460,"cost":60,"name":"Cookies"},
{"id":461,"cost":100,"name":"Pizza"},
{"id":462,"cost":250,"name":"Bread"},
{"id":463,"cost":150,"name":"Sausage"},
{"id":464,"cost":150,"name":"Juice"}];
selectionList = [{"q":1,"r":350,"n":"Coke"}
{"q":2,"r":550,"n":"Bread"}]`
Output :
`[{"id":459,"cost":350,"name":"Coke", "checked" : true,"qty":1},
{"id":460,"cost":60,"name":"Cookies","checked" : false},
{"id":461,"cost":100,"name":"Pizza","checked" : false},
{"id":462,"cost":550,"name":"Bread","checked" : true,"qty":2},
{"id":463,"cost":150,"name":"Sausage","checked" : false},
{"id":464,"cost":150,"name":"Juice","checked" : false}]`
You can improve the time efficiency of this code from O(n²) to O(n) by making use of a hash map to get direct access to a masterList entry by its name:
const masterList = [{"id":459,"cost":250,"name":"Coke"}, {"id":460,"cost":60,"name":"Cookies"}, {"id":461,"cost":100,"name":"Pizza"}, {"id":462,"cost":250,"name":"Bread"}, {"id":463,"cost":150,"name":"Sausage"}, {"id":464,"cost":150,"name":"Juice"}];
const selectionList = [{"q":1,"r":350,"n":"Coke"},{"q":2,"r":550,"n":"Bread"}];
// Create a Map to key the masterList by name
const indexed = new Map(masterList.map(o => [o.name, o]));
// First init the checked property
for (const obj of masterList) obj.checked = false;
// Then iterate and lookup in the Map.
for (const {q, r, n} of selectionList) {
const obj = indexed.get(n); // Lookup happens in constant time
if (!obj) continue;
obj.cost = r;
obj.qty = q || 1;
obj.checked = true;
}
console.log(masterList);
NB: It seems overkill to want to store a checked property, since matched objects already get a qty property which others don't have. In subsequent code you can easily check this with:
checked = 'qty' in obj;
A functional solution using lodash's keyBy to speed up the calculation.
const masterList = [{"id":459,"cost":250,"name":"Coke"}, {"id":460,"cost":60,"name":"Cookies"}, {"id":461,"cost":100,"name":"Pizza"}, {"id":462,"cost":250,"name":"Bread"}, {"id":463,"cost":150,"name":"Sausage"}, {"id":464,"cost":150,"name":"Juice"}];
const selectionList = [{"q":1,"r":350,"n":"Coke"},{"q":2,"r":550,"n":"Bread"}];
const selectionMap = _.keyBy(selectionList, "n");
const output = masterList.map(masterItem => {
// loop through the master list, see if we have it selected.
const selectItem = selectionMap[masterItem.name];
if(selectItem === undefined){
// If it is not checked, return that information.
return {
...masterItem,
checked: false
};
}
// if it is checked, return checked: true and the quantity info which defaults to 1.
return {
...masterItem,
checked: true,
qty: selectItem['q'] || 1
};
});
console.log(output);
Output:
[[object Object] {
checked: true,
cost: 250,
id: 459,
name: "Coke",
qty: 1
}, [object Object] {
checked: false,
cost: 60,
id: 460,
name: "Cookies"
}, [object Object] {
checked: false,
cost: 100,
id: 461,
name: "Pizza"
}, [object Object] {
checked: true,
cost: 250,
id: 462,
name: "Bread",
qty: 2
}, [object Object] {
checked: false,
cost: 150,
id: 463,
name: "Sausage"
}, [object Object] {
checked: false,
cost: 150,
id: 464,
name: "Juice"
}]
In JavaScript code, I have the following enum defined:
MyMessageIds = {
UndefinedId : 0,
FilenameId : 1,
BuildFileId : 2,
MovementArgsId : 3,
MoveId : 4,
ExecuteCommandId : 5
}
In a JavaScript function, I would like to be able to supply the string representation of an enum key (i.e. "MoveId") and return its integer value of 4. So how could I do this?
Just use bracket notation:
var MyMessageIds = {
UndefinedId : 0,
FilenameId : 1,
BuildFileId : 2,
MovementArgsId : 3,
MoveId : 4,
ExecuteCommandId : 5
};
function getValue(key) {
return MyMessageIds[key];
}
You could create some utility methods which take an object (enum) that figures out how to get the keys/values.
var MyMessageIds = {
UndefinedId : 0,
FilenameId : 1,
BuildFileId : 2,
MovementArgsId : 3,
MoveId : 4,
ExecuteCommandId : 5
}
function getEnumKeys(enumType) {
return Object.keys(MyMessageIds);
}
function getEnumValues(enumType) {
return getEnumKeys(enumType).map(function(key) {
return enumType[key];
});
}
function getEnumValue(enumType, key) {
return enumType[getEnumKeys(enumType).filter(function(k) {
return key === k;
}).pop() || ''];
}
document.body.innerHTML = '<pre>' + JSON.stringify({
'Enum Keys' : getEnumKeys(MyMessageIds),
'Enum Vals' : getEnumValues(MyMessageIds),
'Example' : {
'MoveId' : getEnumValue(MyMessageIds, 'MoveId')
}
}, null, 4) + '</pre>';
You could also create your own class object to represent an enum which has reusable methods.
function Enum() {
this.self = arguments[0];
}
Enum.prototype = {
keys : function() {
return Object.keys(this.self);
},
values : function() {
var me = this;
return this.keys(this.self).map(function(key) {
return me.self[key];
});
},
getValueByName : function(key) {
return this.self[this.keys(this.self).filter(function(k) {
return key === k;
}).pop() || ''];
},
getNameByValue : function(value) {
var me = this;
return this.keys(this.self).filter(function(k) {
return me.self[k] === value;
}).pop() || null;
}
};
var MyMessageIds = new Enum({
UndefinedId : 0,
FilenameId : 1,
BuildFileId : 2,
MovementArgsId : 3,
MoveId : 4,
ExecuteCommandId : 5
});
document.body.innerHTML = '<pre>' + JSON.stringify({
'Enum Keys' : MyMessageIds.keys(),
'Enum Vals' : MyMessageIds.values(),
'Example' : {
'MoveId' : MyMessageIds.getValueByName('MoveId'),
'Val(3)' : MyMessageIds.getNameByValue(3)
}
}, null, 4) + '</pre>';
I have a list with that contains a list of objects. Each object has 4 properties on it. There is a checkbox list with the unique values of two of the properties, this helps build my filter array.
the Filter might end up looking like this:
[
{
prop: 'username',
val: ['max', 'sam']
},
{
prop: 'color',
val: ['blue', 'green']
}
]
The list of objects would look something like this:
[
{
username: 'sam',
color: 'blue'
},
{
username: 'jimmy',
color: 'blue'
},
{
username: 'sam',
color: 'black'
},
{
username: 'max',
color: 'green'
},
{
username: 'max',
color: 'blue'
}
]
The Desired Result
[
{
username: 'sam',
color: 'blue'
},
{
username: 'max',
color: 'green'
},
{
username: 'max',
color: 'blue'
}
]
I feel like I'm going down a never ending forEach rabbit hole. I'm guessing I need some sort of recursion. Currently here is what I have:
var temporary = scope.transactions;
function getFilteredTransactions() {
var filter = deviceFilterService.get();
if (filter.length > 0) {
var temp2 = [];
angular.forEach(filter, function (fil) {
//object
angular.forEach(fil.val, function (filterValue) {
//list on each object
angular.forEach(temporary, function (transaction) {
if (transaction[fil.prop] === filterValue) {
if (temp2.indexOf(transaction) === -1) {
temp2.push(transaction);
}
}
});
temporary = temp2;
});
});
$log.debug(temporary);
scope.transactions = temporary;
} else {
initialize();
}
}
This is starting to work, the second time it goes through the property for color it ends up just wanting to add the exact same transaction to the temp2 array. There has to be a better way to set this up, possibly through recursion.
If you convert the format of the first list to a dictionary, i think if should get easier.
var dict = {};
angular.forEach(source1, function(ob){
dict[ob.prop] = ob.val;
});
function getFiltered(ob){
for(var prop in ob){
if(dict[prop] && dict[prop].indexOf(ob[prop]) === -1){
return false;
}
}
return true;
};
and just call it as:
var temporary = scope.transactions.filter(getFiltered);
Demo
Basically the first part converts:
[
{
prop: 'username',
val: ['max', 'sam']
},
{
prop: 'color',
val: ['blue', 'green']
}
];
to:
{
username:['max', 'sam'],
color:['blue', 'green']
}
so that it makes the look up much easier.
You might want to change the variable names here for clarity, but this will do what you're asking for:
var values = {};
angular.forEach(startingData, function(rawData) {
angular.forEach(rawData, function(value, key) {
if (angular.isUndefined(values[key])) {
values[key] = [];
}
if (values[key].indexOf(value) === -1) {
values[key].push(value);
}
})
});
var result = [];
angular.forEach(values, function(value, key) {
result.push({prop: key, val: value})
});
You can simply iterate each key of the data the needs filtering, find the appropriate filter per that key, and check the value against the filter values:
$scope.transactions = $scope.transactions.filter(isItemValidFilter);
function isItemValidFilter(item) {
var filters = deviceFilterService.get();
//For each property in the data, get the correct filter from the list of filters
var totalConditions = Object.keys(item).length;
var correctConditions = 0;
for (var filterKey in item) {
var correctFilters = filters.filter(function(dataFilter) {
return dataFilter.prop == filterKey
});
if (correctFilters.length) {
//Ill assume only 1 filter, so just use the 0 index
var correctFilter = correctFilters[0];
var conditions = correctFilter.val;
if (conditions && conditions.length) {
//check the values!
if (conditions.indexOf(item[filterKey]) > -1) {
correctConditions++;
}
}
}
}
return correctConditions === totalConditions;
}
Demo: http://jsfiddle.net/Lz32hka5/1/
Try:
var temp2 = [], matched;
angular.forEach(temporary, function(item){
matched = true;
angular.forEach(Object.keys(item), function(key){
angular.forEach(filter, function(filter){
filter.prop == key && filter.val.indexOf(item[key]) == -1 && (matched = false);
});
});
matched && temp2.push(item);
});
console.log(temp2)
temporary is the list of objects, filter: your filters
Demo: http://jsfiddle.net/wZVanG/7wnae850/
I have a problem with getting two 2D arrays to look alike.
The arrays look like this:
groupedObjects: [
{ value: 125, currency: "EUR" },
{ value: 100, currency: "USD" },
{value: 320, currency: "RON" }
]
groupedObjects1: [
{ value: 500, currency: "EUR" },
{ value: 280, currency: "RON" }
]
or
groupedObjects: [
{ value: 125, currency: "EUR" }
]
groupedObjects1: [
{ value: 500, currency: "EUR" },
{ value: 280, currency: "RON" }
]
or
groupedObjects: [
{ value: 125, currency: "EUR" }
]
groupedObjects1: [
{ value: 500, currency: "EUR" }
]
or
groupedObjects: [
{ value: 125, currency: "EUR" }
]
groupedObjects1: [
]
The arrays could be of any size, with unlimited currencies, but not always equal as length.
How can I have those 2 arrays look identical? In case if one array does not include one currency, I would like that the array would have an element with {value: 0, currency: 'missing currency'} and all currencies in both arrays must be arranged in ascending order, sorted by currency
Any ideas?
Thank you in advance
Later edit:
The code I tried looks like:
if ($(groupedObjects).length > $(groupedObjects1).length) {
var newArray = [];
$.each(groupedObjects, function (key, value) {
var flag = false;
var breakout = false;
flag = (function () {
$.each(groupedObjects1, function (key1, value1) {
if (value.currency === value1.currency) {
newArray.push(value1);
breakout = true;
return false;
}
});
if (!breakout) {
return true;
}
})();
if (flag) {
newArray.push({
value: 0,
currency: value.currency
});
}
});
groupedObjects1 = newArray;
} else if ($(groupedObjects).length < $(groupedObjects1).length) {
var newArray = [];
$.each(groupedObjects1, function (key, value) {
var flag = false;
var breakout = false;
flag = (function () {
$.each(groupedObjects, function (key1, value1) {
if (value.currency === value1.currency) {
newArray.push(value1);
breakout = true;
return false;
}
});
if (!breakout) {
return true;
}
})();
if (flag) {
newArray.push({
value: 0,
currency: value.currency
});
}
});
groupedObjects = newArray;
} else if ($(groupedObjects).length == $(groupedObjects1).length) {
var newArray = [];
var oldArray = [];
oldArray = groupedObjects;
console.info('>>> Initial object 1');
console.info(groupedObjects);
$.each(groupedObjects1, function (key, value) {
var flag = false;
var breakout = false;
flag = (function () {
$.each(groupedObjects, function (key1, value1) {
if (value.currency === value1.currency) {
newArray.push(value1);
breakout = true;
return false;
}
});
if (!breakout) {
return true;
}
})();
if (flag) {
newArray.push({
value: 0,
currency: value.currency
});
}
});
//groupedObjects = newArray;
$.merge(groupedObjects, newArray);
var newArray = [];
$.each(oldArray, function (key, value) {
var flag = false;
var breakout = false;
flag = (function () {
$.each(groupedObjects1, function (key1, value1) {
if (value.currency === value1.currency) {
newArray.push(value1);
breakout = true;
return false;
}
});
if (!breakout) {
return true;
}
})();
if (flag) {
newArray.push({
value: 0,
currency: value.currency
});
}
});
groupedObjects1 = newArray;
//$.merge(groupedObjects1, newArray);
}
I am not exactly sure what's your final goal, but here's an example:
var arr1 = [
{ value: 125, currency: "EUR" },
{ value: 100, currency: "USD" },
{ value: 320, currency: "RON" }
],
arr2 = [
{ value: 500, currency: "EUR" },
{ value: 280, currency: "RON" }
];
var appendMissingCurrencies = (function () {
return function (dest, source) {
//Use an object map for quick lookups
var existingCurrenciesMap = currencyMapFrom(dest);
source.forEach(function (item) {
var currency = item.currency,
currencyMissing = !existingCurrenciesMap[currency];
if (currencyMissing) {
existingCurrenciesMap[currency] = true;
appendMissingCurrency(dest, currency);
}
});
};
function appendMissingCurrency(currencies, currency) {
currencies.push({ value: 0, currency: currency });
}
function currencyMapFrom(currencies) {
return currencies.reduce(function (map, item) {
if (!map[item.currency]) map[item.currency] = true;
return map;
}, {});
}
})();
function sortCurrencies(currencies) {
return currencies.sort(function (a, b) {
var currencyOrder = a.currency < b.currency? -1 : +(a.currency > b.currency);
return currencyOrder? currencyOrder : a.value - b.value;
});
}
appendMissingCurrencies(arr1, arr2);
appendMissingCurrencies(arr2, arr1);
arr1 = sortCurrencies(arr1);
arr2 = sortCurrencies(arr2);
Since currency is the driving force behind the desired algorithm. The first step is to find the common set of currencies between the two arrays and sort it.
container.currencies = (function(){
var array = [];
var func = function(arrayKey){ return function(key,value){
array.push(value.currency);
container.map[arrayKey][value.currency] = value.value;
}
};
$.each(a, func('a'));
$.each(b, func('b'));
return array.filter(function(elem, pos) {
return array.indexOf(elem) == pos;
}).sort()
})();
At the same time we are determining the unique set of currencies, we are also building up a contextual data mapping -- container holds the data context and a / b represent the original arrays and are also used as a property key under which each original arrays contents are stored.
If empty container would look like: {map: {a:{}, b:{}}, currencies:[]}
Once our data context is built, we need to roll it back out into arrays.
container.getNormalizedArrays = function(){
var arrays = {a: [], b: []};
function normalizer(target, currency){
var value = container.map[target][currency]
arrays[target].push({value: value?value:0, currency: currency})
}
$.each(container.currencies, function(key, value){
normalizer('a', value);
normalizer('b', value);
})
return arrays;
}
Check out my fiddle for full working example.