let obj = { T: 1, h: 1, i: 3, s: 3, t: 1, r: 1, n: 1, g: 1 };
In the given object we have 2 keys(i & s both have same value 3) contains same value. I need to keep one key/value pair and another wants to remove.
How we can achieve this?
Make a copy and leave one field out?
let obj2 = { T: obj.T, h: obj.h, i: obj.i, t: obj.t, r: obj.r, n: obj.n, g: obj.g };
;-)
It's not super clear if you want to omit all keys with duplicate values or just some keys. In case you want the latter, here's a reusable function for that:
function copyWithout(source, ...keysToOmit) {
return Object.entries(source).reduce(
(accumulator, [key, value]) => {
return keysToOmit.includes(key)
? accumulator
: Object.assign(accumulator, { [key]: value });
},
Object.create(null) // or just {}
);
}
And use it like so:
copyWithout(obj, 'i');
Can be used to omit multiple keys as well:
copyWithout(obj, 'i', 't', 'r');
You can loop over object keys and add values in some temp array. Verify from that temp array and if value already exist in it then delete that key from object. Try like below.
let obj = { T: 1, h: 1, i: 3, s: 3, t: 1, r: 1, n: 1, g: 1 };
let val = [];
// loop over keys and verify that if value is repeating then delete that key from object.
Object.keys(obj).forEach(k => {
if (val.includes(obj[k])) {
delete obj[k];
} else {
val.push(obj[k])
}
});
console.log(obj);
Alternatively if you wish to keep your object as it is and want output in another object then try like below.
let obj = { T: 1, h: 1, i: 3, s: 3, t: 1, r: 1, n: 1, g: 1 };
let val = [];
let newObj = {};
// loop over keys and verify that if value is repeating then delete that key from object.
Object.keys(obj).forEach(k => {
if (!Object.values(newObj).includes(obj[k])) {
newObj[k] = obj[k];
}
});
console.log('newObj', newObj);
console.log('obj', obj);
You can use the following and swap key values twice,
let obj = { T: 1, h: 1, i: 3, s: 3, t: 1, r: 1, n: 1, g: 1 };
function swapKV(ob) {
return Object.entries(ob).reduce((p,[key,value]) => ({...p,[value]: key}),{})
}
swapKV(swapKV(obj))
You could save all unique values in set. This is the most optimal solution since it takes O(n) in time.
let obj = { T: 1, h: 1, i: 3, s: 3, t: 1, r: 1, n: 1, g: 1 };
function removeDuplicateValues(obj) {
const uniqueValues = new Set();
const uniquePairs = {};
Object.keys(obj).forEach(key => {
const curValue = obj[key];
if (!uniqueValues.has(curValue)) {
uniqueValues.add(curValue);
uniquePairs[key] = curValue;
}
});
return uniquePairs;
}
console.log(removeDuplicateValues(obj)) // {T: 1, i: 3}
const object = { a: 1, b: 2, c: 3, d: 2, e: 3 };
const removeUniqueValuesFromObject = (object) => {
const map = new Map();
// loop through all the attributes on an object
for (const [key, value] of Object.entries(object)) {
// check if the value already exists in map, if yes delete the attribute
if (map.has(value)) {
delete object[`${key}`];
continue;
}
// if value not found, add it to the map
map.set(value, key);
}
// return the updated object
return object;
};
const result = removeUniqueValuesFromObject(object);
console.log(result);
Related
Here is the task
#param {object} ???
#returns {object} an object with the given object's values as keys, and keys as values. If there are duplicate values in the input object, only the first key-value pair should be used and subsequent pairs with the same value discarded.
2.Here is what i did code
What's wrong with it? How should i rework?
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
function swapPairs2(obj){
let newObject = {};
for(const key in obj){
let value = obj[key]
newObject[value] = key;
}
return newObject;
}
3.Here are the tests
test(swapPairs2(object5), { 1: "a", 2: "b", 3: "c" });
test(swapPairs2(object6), { 1: "a" });
4.Here is the error i got
I am supposed to get the first key-value pair, but a random return.
You can also reverse the entries.
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = obj => Object.fromEntries(
Object.entries(obj)
.reverse() // Keep the first, not the last
.map(entry => [entry[1], entry[0]]) // Reverse the key and vlaue
);
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Only add the value/key pairs, if the value doesn't already exist on newObject by checking with the in operator:
function swapPairs2(obj) {
const newObject = {};
for (const key in obj) {
const value = obj[key];
if(!(value in newObject)) {
newObject[value] = key;
}
}
return newObject;
}
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Another option is to use Array.reduce():
const swapPairs2 = obj =>
Object.entries(obj)
.reduce((acc, [k, v]) =>
v in acc
? acc
: { ...acc, [v]: k }
, {})
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
You can do it by taking the object keys and using the Array.reduce() method on the keys.
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = (obj) => Object.keys(obj)
.reduce((a, c) => (a[obj[c]] = a[obj[c]] ?? c, a), {});
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
Or using the Array.reduce() method on the Object.entries()
const object5 = { a: 1, b: 2, c: 3, d: 1 };
const object6 = { a: 1, b: 1, c: 1, d: 1 };
const swapPairs2 = obj => Object.entries(obj)
.reduce((acc, [key, val]) => (acc[val] = acc[val] ?? key, acc), {})
console.log(swapPairs2(object5));
console.log(swapPairs2(object6));
So I have an array of objects with some varying number of properties (but the property names are known), for example:
let data = [{a: 10, b: 1, c:10},
{a: 17, b: 2, c:16},
{a: 23, b: 3, c:41}]
I need to construct an object that sums up the values in the respective properties, so in this example I'd need to construct an object {a: 50, b: 6, c:67}
I wrote the following function to do this:
calcTotalForDataProps(data, props) {
let summedData = {}
for (const prop of props) {
summedData[prop] = 0;
}
data.forEach((dataObj) => {
for (const prop of props) {
summedData[prop] += dataObj[prop];
}
});
return summedData;
}
And you call it like this:
calcTotalForDataProps(data, ['a', 'b', 'c']);
But I'm wondering if there's a much shorter way to write this with ES6?
You could map the wanted props with their new sums for getting an object as result.
function calcTotalForDataProps(data, props) {
return data.reduce((r, o) => Object
.fromEntries(props.map(k => [k, (r[k] || 0) + o[k]])
), {});
}
const data = [{ a: 10, b: 1, c: 10}, { a: 17, b: 2, c: 16 }, { a: 23, b: 3, c: 41 }]
console.log(calcTotalForDataProps(data, ['a', 'b', 'c']));
There's no need to iterate over the properties initially - you can create the property inside the other loop if it doesn't exist yet.
let data = [{a: 10, b: 1, c:10},
{a: 17, b: 2, c:16},
{a: 23, b: 3, c:41}]
const calcTotalForDataProps = (data, props) => {
const summedData = {};
for (const obj of data) {
for (const [prop, num] of Object.entries(obj)) {
summedData[prop] = (summedData[prop] || 0) + num;
}
}
return summedData;
}
console.log(calcTotalForDataProps(data));
Implement the sumObjects function, which takes an unlimited number of objects and returns an object that combines all the objects entered.
Notes:
All object properties will have numeric values only
If the object keys match, the values of the corresponding keys are summed
The function always returns an object
The numbers in the object can be positive or negative
You can use the rest operator to collect arguments passed to a function in one array
Examples:
const obj = {};
sumObjects() === {}
sumObjects(obj) === {}
and
const first = {a: 2, b: 4};
const second = {a: 2, b: 10};
const third = {d: -5};
sumObjects(first) === {a: 2, b: 4}
sumObjects(first, third) === {a: 2, b: 4, d: -5}
sumObjects(first, second, third) === {a: 4, b: 14, d: -5}
This is my code.
function sumObjects(...params) {
let C = Object.fromEntries(
Object.keys(params[0])
.concat(Object.keys(params[1]))
.map(k => [k,
(params[0][k] || 0) + (params[1][k])
])
)
return C
}
I don't know how to add all these objects into one.
The bottom line is that I need to combine all the objects, but I just don't know how to do this, I can't find anything.
You can iterate over the param objects using .reduce and for each object, set/update its properties in acc:
function sumObjects(...params) {
return params.reduce((acc,item) => {
Object.entries(item).forEach(([property,value]) => {
const prev = acc[property];
acc[property] = prev ? prev+value : value;
});
return acc;
}, {});
}
const first = { a: 2, b: 4 };
const second = { a: 2, b: 10 };
const third = { d: -5 };
console.log( sumObjects(first) );
console.log( sumObjects(first, third) );
console.log( sumObjects(first, second, third) );
You could reduce the array of objects and iterate the entries of a single object.
const
sumObjects = (...objects) => objects.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => r[k] = (r[k] ||0) + v);
return r;
}, {}),
first = { a: 2, b: 4 },
second = { a: 2, b: 10 },
third = { d: -5 };
console.log(sumObjects(first)); // {a: 2, b: 4}
console.log(sumObjects(first, third)); // {a: 2, b: 4, d: -5}
console.log(sumObjects(first, second, third)); // {a: 4, b: 14, d: -5}
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object like:
Object {v: 1, b: 1, n: 1, m: 1, c: 2, d: 3}
I am trying to collect keys that has same values and generate an array (so in this case [v, b, n, m].
So if I know 1, how can I get v,b,n,m in an array?
You can use Object.keys() to get key values of the object, then filter values based on your condition using filter()
var obj = {
v: 1,
b: 1,
n: 1,
m: 1,
c: 2,
d: 3
},
val = 1;
var res = Object.keys(obj).filter(function(v) {
return obj[v] == val;
});
document.write('<pre>' + JSON.stringify(res, null, 4) + '</pre>');
Performance? Simple for...in loop.
var myObj = {v: 1, b: 1, n: 1, m: 1, c: 2, d: 3};
function collect (obj, value) {
var arr = [];
for (var key in obj)
if (obj.hasOwnProperty(key) && obj[key] === value)
arr.push(key);
return arr;
}
console.log(collect(myObj, 1));
Fancy? Object.keys, and Array.prototype.filter.
var myObj = {v: 1, b: 1, n: 1, m: 1, c: 2, d: 3};
function collect (obj, value) {
return Object.keys(obj).filter(key => obj[key] === value);
}
console.log(collect(myObj, 1));
using the underscore.js library you can do the following
var object = {v: 1, b: 1, n: 1, m: 1, c: 2, d: 3};
var output = _.map(object, function(value, key){
if(value == 1){
return key
}
}).filter(function(value){
return value;
});
console.log(output);
I have two object literals like so:
var firstObject =
{
x: 0,
y: 1,
z: 2,
a: 10,
b: 20,
e: 30
}
var secondObject =
{
x: 0,
y: 1,
z: 2,
a: 10,
c: 20,
d: 30
}
I want to get the intersection of the keys these two object literals have like so:
var intersectionKeys = ['x', 'y', 'z', 'a']
I can obviously do a loop and see if a key with the same name exists in the other object, but I am wondering if this would be a good case for some functional programming and map / filter / reduce usage? I myself have not done that much functional programming, but I have a feeling, that there could exist a clean and clever solution for this problem.
A solution without indexOf.
var firstObject = { x: 0, y: 1, z: 2, a: 10, b: 20, e: 30 },
secondObject = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 };
function intersection(o1, o2) {
return Object.keys(o1).concat(Object.keys(o2)).sort().reduce(function (r, a, i, aa) {
if (i && aa[i - 1] === a) {
r.push(a);
}
return r;
}, []);
}
document.write('<pre>' + JSON.stringify(intersection(firstObject, secondObject), 0, 4) + '</pre>');
Second attempt with O(n).
var firstObject = { x: 0, y: 1, z: 2, a: 10, b: 20, e: 30 },
secondObject = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 };
function intersection(o1, o2) {
return Object.keys(o1).filter({}.hasOwnProperty.bind(o2));
}
document.write('<pre>' + JSON.stringify(intersection(firstObject, secondObject), 0, 4) + '</pre>');
The given answers are nice and astonishing but there could be a problem in void's answer and that is:
"What if one of property values intentionally set to undefined."
Nina's answer is good (really fantastic) but as we are in era of fun JavaScript I think mine wont be too bad:
var a = { x: undefined, y: 1, z: 2, a: 10, b: 20, e: 30 }
var b = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 }
function intersect(o1, o2){
return Object.keys(o1).filter(k => Object.hasOwn(o2, k))
}
document.write('<pre>' + JSON.stringify(intersect(a, b)) + '</pre>');
Update
onalbi mentioned some performance issue in comments which is rational and therefore the code bellow seems to be a better way to handle the problem:
var a = { x: undefined, y: 1, z: 2, a: 10, b: 20, e: 30};
var b = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30};
function intersect(o1, o2) {
const [k1, k2] = [Object.keys(o1), Object.keys(o2)];
const [first, next] = k1.length > k2.length ? [k2, o1] : [k1, o2];
return first.filter(k => k in next);
}
document.write('<pre>' + JSON.stringify(intersect(a, b)) + '</pre>');
The procedure i will suggest is:
Get the array of keys using Object.keys() for one of the objects.
Find the intersection the array using .filter and checking if the second object contains a key matching the first array.
var firstObject = {
x: 0,
y: 1,
z: 2,
a: 10,
b: 20,
e: 30
}
var secondObject = {
x: 0,
y: 1,
z: 2,
a: 10,
c: 20,
d: 30
}
function getIntKeys(obj1, obj2){
var k1 = Object.keys(obj1);
return k1.filter(function(x){
return obj2[x] !== undefined;
});
}
alert(getIntKeys(firstObject, secondObject));
Recursive function
This is other solution, maybe help you. I used a recursive function to intercept two objects. The advantage of this solution is that you not need worry about attributes that are objects at same time.
In this case the function intercept attributes that exist in both objects and asign the value of 'objSource' like final value of attribute intercepeted.
{
function interceptObjects(objSource, objInterface) {
let newObj = {};
for (const key in objSource) {
if (objInterface.hasOwnProperty(key)) {
// in javascript an array is a object too.
if (objSource[key] instanceof Object && !Array.isArray(objSource[key]) && objInterface[key] instanceof Object && !Array.isArray(objInterface[key])) {
newObj[key] = {};
newObj[key] = interceptObjects(objSource[key], objInterface[key])
} else {
newObj[key] = objSource[key];
}
}
}
return newObj;
}
// FOR TESTING
let objSource = {
attr1: '',
attr2: 2,
attr3: [],
attr4: {
attr41: 'lol',
attr42: 12,
attr43: 15,
attr45: [1, 4],
},
attr5: [2, 3, 4],
};
let objInterface = {
attr1: null,
attr4: {
attr41: null,
attr42: 12,
attr45: [1],
},
attr5: [],
attr6: null,
};
console.log(this.interceptObjects(objSource, objInterface));
}
Here is a simple entry, very functional, handles any number of objects, and returns the values of the matching keys from the first object passed.
This behavior is similar to that of array_intersect_key() in PHP in case anyone is searching for that.
function intersectKeys(first, ...rest) {
const restKeys = rest.map(o => Object.keys(o));
return Object.fromEntries(Object.entries(first).filter(entry => restKeys.every(rk => rk.includes(entry[0]))));
}
Expanded here for better explanation and commenting
function intersectKeys(first, ...rest) {
// extract the keys of the other objects first so that won't be done again for each check
const restKeys = rest.map(o => Object.keys(o));
// In my version I am returning the first objects values under the intersect keys
return Object.fromEntries(
// extract [key, value] sets for each key and filter them, Object.fromEntries() reverses this back into an object of the remaining fields after the filter
Object.entries(first).filter(
// make sure each of the other object key sets includes the current key, or filter it out
entry => restKeys.every(
rk => rk.includes(entry[0])
)
)
);
// to get JUST the keys as OP requested the second line would simplify down to this
return Object.keys(first).filter(key => restKeys.every(rk => rk.includes(key)));
}
It's important to note that this solution only works on string keys, Symbol keys will be ignored and the final object will not contain any. Though a similar function could be written to compare Symbol intersect as well.
I know this is an old post, however, I want to share a solution I wrote today that I believe is efficient and clean.
function intersectingKeys(...objects) {
return objects
.map((object) => Object.keys(object))
.sort((a, b) => a.length - b.length)
.reduce((a, b) => a.filter((key) => b.includes(key)));
}
This function can take in n number of objects, and find the intersecting keys.
This is how it works.
Map the objects, creating an array of key arrays.
Sort the array by length, this puts the smallest key arrays first.
Finally, reduce our key arrays, by filtering each list of keys against the next list.
I think the clever part of this algorithm is the pre sorting of the key arrays. By starting with the smallest list of keys, we have less work to do comparing keys.
Here is the usuage:
var firstObject = {
x: 0,
y: 1,
z: 2,
a: 10,
b: 20,
e: 30,
};
var secondObject = {
x: 0,
y: 1,
z: 2,
a: 10,
c: 20,
d: 30,
};
intersectingKeys(firstObject, secondObject);
// [ 'x', 'y', 'z', 'a' ]