How to get unique values from an array of JS objects? - javascript

I need some help figuring out how to get unique values from an array of objects in this format. I have reviewed several examples but none had the values of a key being an array. Thanks in advance for your advice.
I have a JS array with complex objects with some object values being arrays:
let array = [
{ key1: ["one","two","four"], key2: [1,2,4] },
{ key1: ["two","four","six"], key2: [2,4,6] },
{ key1: "nine", key2: 9 },
];
I want to get the unique values for all keys in the object (so it will support any structure) and output like this:
[
{
key1: ["one","two","four", "six", "nine"],
key2: [1,2,4,6,9]
}
]

You can try this :
let array = [
{ key1: ["one","two","four"], key2: [1,2,4] },
{ key1: ["two","four","six"], key2: [2,4,6] },
{ key1: "nine", key2: 9 },
];
let keys = [...new Set(array.flatMap(d => Object.keys(d)))];
let valuesAsObj = Object.fromEntries(
keys.map(k => [
k,
[... new Set(array.flatMap(d => d[k] ? d[k] : null).filter(v => v != null && v != undefined))]
])
);
let valuesAsArr = keys.map(k => [... new Set(array.flatMap(d => d[k] ? d[k] : null).filter(v => v != null && v != undefined))])
console.log(valuesAsObj);
console.log(valuesAsArr);

I approached it by creating a Set for each unique property and adding all the properties to that set.
let array = [
{ key1: ["one","two","four"], key2: [1,2,4] },
{ key1: ["two","four","six"], key2: [2,4,6] },
{ key1: "nine", key2: 9 },
];
const result = {};
array.forEach(a => {
for (let key in a) {
if (!result[key]) result[key] = new Set();
let v = a[key];
if (!Array.isArray(v)) v = [v];
v.forEach(result[key].add, result[key]);
}
});
console.log(result );
const overlyComplicatedResult = Object.entries(result).map(([key,val]) => ({[key]: [...val]}));
console.log(overlyComplicatedResult);
I'm personally happy with the results in the intermediate format that they are stored in the result variable, which is just a single object where each property is one of the keys and the value of each property is a Set of all the distinct values.
But, if you really want to transform that into your overly complicated result, where you have an array of objects, each with a single property, and the unique items are stored in an array instead of a Set, then the code is provided above as a final transformation step using Object.entries.

You could try something like this:
let array = [
{ key1: ["one","two","four"], key2: [1,2,4] },
{ key1: ["two","four","six"], key2: [2,4,6] },
{ key1: "nine", key2: 9 },
];
let obj = {};
array.forEach((item) =>
Object.entries(item).forEach((entry) => {
if (!obj[entry[0]]) obj[entry[0]] = [];
!Array.isArray(entry[1])
? obj[entry[0]].push(entry[1])
: (obj[entry[0]] = [...obj[entry[0]], ...entry[1]]);
})
);
/*ADDED LOGIC TO HAVE UNIQUE VALUES IN THE OBJECT*/
Object.entries(obj).forEach(
(entry) => (obj[entry[0]] = Array.from(new Set(entry[1])))
);
console.log(obj);
let result = [];
Object.entries(obj).forEach((entry) =>
result.push({
[entry[0]]: Array.from(new Set(entry[1])),
})
);
console.log(result);

Related

How to update a key/value pair in a nested array of objects in Javascript

this is my data structure:
[
0:
key1: value,
key2: value,
array:
0:
thisId: xxxxx,
thisValue: value,
1:
notThisId: someId,
notThisValue: value,
key3: value
1:
key1: value
key2: value
array:
0:
anotherId: id
anotherValue: value
key3: value
]
Hello, I have a query with is returning:
thisIdRef: xxxxx,
thisNewValue: newValue
Is it possible to update the nested 'thisValue' to 'thisNewValue' where 'thisIdRef' is equal to 'thisId', or 'xxxxx'?
I have done something similar below using findIndex and splice, but this is for a non-nested key/value pair and I can't work out how to find a nested id, or indeed if it's possible.
let newArray = oldArray;
const index = newArray.findIndex(post => post._id === editedPostId)
newArray.splice(index, 1, {
...newArray[index],
post: editedContent
})
Any help very much appreciated.
I will assume you want to create a new array, such that the original array and its nested structure is not mutated.
Here is a function that you could use:
function setDeep(original, editedPostId, editedContent) {
return original.map(obj => {
let i = obj.array.findIndex(item => item.thisId === editedPostId);
if (i == -1) return obj;
return {
...obj,
array: Object.assign([], obj.array, {
[i]: {
...obj.array[i],
thisId: editedPostId,
thisValue: editedContent
}
})
};
});
}
// Example call
let original = [{
key1: 1,
key2: 2,
array: [{
thisId: "xxxxx",
thisValue: 3,
}, {
notThisId: "yyyy",
notThisValue: 4,
}],
key3: 5
}, {
key1: 6,
key2: 7,
array: [{
anotherId: "zzzzz",
anotherValue: 8
}],
key3: 9
}];
let editedPostId = "xxxxx";
let editedContent = 42;
console.log(setDeep(original, editedPostId, editedContent));
Note that the code you have given for a non-nested structure seems to create a new array, but it still mutates the original array. When you want the original to remain in tact, you have to take care to deep-copy the parts that are affected.
The general form would be:
const found = parentArray.find(it => it.array[0] === theId)
if (found) {
found.array[1] = theValue
}

Transform nested array of objects to an object

I am trying to convert a nested array to an object, I made several attempts but without success.
these are the data i have
[
['key1', { childKey1: "text" }, { childKey2: "text" }],
['key2', { childKey1: "text" }, { childKey2: "text" }]
]
this is the data i need
{
key1: {
childKey1: "text",
childKey2: "text"
},
key2: {
childKey1: "text",
childKey2: "text"
}
}
Map the data to entries - [[key, value], [key, value]]. Use destructuring to get the key (the 1st item in a sub-array), and rest syntax to get an array of objects. Merge the array of objects by spreading the array of objects into Object.assign() to get the value. Use Object.fromEntries() to convert the entries to an object.
const data = [['key1', {childKey1: "text"}, {childKey2: "text"}], ['key2', {childKey1: "text"}, {childKey2: "text"}]]
const result = Object.fromEntries(data.map(
([k, ...v]) => [k, Object.assign({}, ...v)]
))
console.log(result)
You may try reduce to achieve such task. You could do something like this:
const data = [
["key1", { childKey1: "text" }, { childKey2: "text" }],
["key2", { childKey1: "text" }, { childKey2: "text" }]
];
function dataToJSON(data) {
return data.reduce((acc, current) => {
const [key, ...values] = current;
acc[key] = Object.assign({}, ...values);
return acc;
}, {});
}
console.log(dataToJSON(data));
Works perfect for any number of children. You have to be careful with repeated keys not to overwrite a previous one.
.reduce() method is another way to approach this.
const arr = [['key1', {childKey1: "text"}, {childKey2: "text"}], ['key2', {childKey1: "text"}, {childKey2: "text"}]];
const newArr = arr.reduce((accumulator, currVal) => {
const [key, ...values] = currVal; // destruct array and separate key from all other values
accumulator[key] = Object.assign({}, ...values); // assign values to your final object, using the key extracted above
return accumulator;
}, {});
console.log(newArr);
I know this was already answered, but here's my attempt at it:
let jsonArray = [['key1', {childKey1: "text"}, {childKey2: "text"}], ['key2', {childKey1: "text"}, {childKey2: "text"}]];
let newJson = {};
for(var i=0; i<jsonArray.length; i++){
newJson[jsonArray[i][0]] = Object.assign({}, ...jsonArray[i].slice(1));
}
console.log(newJson);

Return object has condition true from object has key

I have question about object. I want to filter to return list of keys has condition true in this object below:
myObject = {
key1: {
name:"key1",
select:true
},
key2: {
name:"key2",
select:false
},
key3: {
name:"key3",
select:true
}
}
For some reason, I have to add specific key for it. Now I want to return an array has seclect true.
arrayWantreturn = ["key1", "key3"]
Thank you so much for your help.
You can make a loop with that object:
var myObject = {
key1: {
name:"key1",
select:true
},
key2: {
name:"key2",
select:false
},
key3: {
name:"key3",
select:true
}
};
var arr = [];
for (var prop in myObject) {
if (myObject[prop].select) {
arr.push(myObject[prop].name);
}
}
console.log(arr);
Maybe you can try the following:
const myObject = {
key1: {
name:"key1",
select:true
},
key2: {
name:"key2",
select:false
},
key3: {
name:"key3",
select:true
}
};
const result = Object.entries(myObject)
.filter(([k, v]) => v['select'])
.flatMap(e => e[1].name);
console.log(result);
I hope that helps!
You can get the values of your object using Object.values() and then keep all of the objects which have a select of true by using .filter(). You can then .map() each remaining object to its name property like so:
const myObject = { key1: { name:"key1", select:true }, key2: { name:"key2", select:false }, key3: { name:"key3", select:true } };
const res = Object.values(myObject)
.filter(({select}) => select)
.map(({name}) => name);
console.log(res);
This uses destructuring assignment to extract the property values in the callback method arguments.

Is it possible to assign an object while destructuring another (WHILE using defaults in destructuring)

My end goal is to be able to assign a new object using the spread operator from an object that is destructured while assigning default values (if they don't exist).
It looks like this may not be possible how I'd like it. Here's my expectations and attempts:
//Expected beginning object
const a1 = {
key1: "test1",
key2: "test2",
};
//Expected end result
const b1 = {
key1: "test1",
key2: "test2",
key3: {
nestedKey1: "nestedVal1",
},
};
//Expected beginning object
const a2 = {
key1: "test1",
key2: "test2",
key3: {
nestedKey1: "actualValue",
}
}
//Expected end result
const b2 = {
key1: "test1",
key2: "test2",
key3: {
nestedKey1: "actualValue",
},
};
Snippet 1: Does not assign default values.
const a = {
key1: "test1",
key2: "test2",
}
const {...b} = {
key1,
key2,
key3: {
nestedKey1: nestedKey1 = "nestedVal1",
} = {},
key4 = 'someDefault'
} = a
console.log(b); // does not have key3, or key4
console.log(key4); //this exists as a const, key3 does not
Snippet 2: Functional, but can become problematic if multiple levels of nesting are required.
const a = {
key1: "test1",
key2: "test2",
}
let {
key1,
key2,
key3: {
nestedKey1: nestedKey1 = "nestedVal1",
} = {},
} = a
const key3 = a.key3 || {}; // is it possible to include this in the destructuring? Specifically in the destructuring which defines the default.
const b = {
key1,
key2,
key3: {
...key3,
nestedKey1,
}
}
console.log(b);
Snippet 2 (showing that nested objects aren't overwritten)
const a = {
key1: "test1",
key2: "test2",
key3: {
someOtherKey: "itWorks",
}
}
let {
key1,
key2,
key3: {
nestedKey1: nestedKey1 = "nestedVal1",
} = {},
} = a
const key3 = a.key3 || {};
const b2 = {
key1,
key2,
key3: {
...key3,
nestedKey1,
}
}
console.log(b2);
Your confusion arises from the similarity of destructuring syntax to that of object declaration syntax.
This is object declaration:
const a = {
key1: "test1",
key2: "test2",
}
This is destructuring
const { key1, key2 } = a
console.log(key1) // => test1
console.log(key2) // => test2
Next, you need to remember that assignment operator = is right associative in JavaScript. So something like
let a = b = 1
means assign value 1 to b, then to a.
Next, if you spread all values into a single var, you're essentially doing a simple assignment with a fancy ES6 syntax. So:
const {...b} = a
// is same as
const b = a
Combining the above 3 effects, what you get is effectively a multiple assignment:
const {...b} = a
const {key1, key2} = a
Multiple assignment can be confusing and there are linting rules to prevent such issues: https://eslint.org/docs/rules/no-multi-assign
So the answer to your question is simply, No! :)
It'd need at least 2 lines to do so - one to collect props with defaults while destructuring, and another to create the next object with those props.

Getting JavaScript object key list

I have a JavaScript object like
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
}
How can I get the length and list of keys in this object?
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
}
var keys = Object.keys(obj);
console.log('obj contains ' + keys.length + ' keys: '+ keys);
It's supported on most major browsers now.
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
};
var keys = [];
for (var k in obj) keys.push(k);
console.log("total " + keys.length + " keys: " + keys);
Underscore.js makes the transformation pretty clean:
var keys = _.map(x, function(v, k) { return k; });
Edit: I missed that you can do this too:
var keys = _.keys(x);
If you only want the keys which are specific to that particular object and not any derived prototype properties:
function getKeys(obj) {
var r = []
for (var k in obj) {
if (!obj.hasOwnProperty(k))
continue
r.push(k)
}
return r
}
e.g:
var keys = getKeys({'eggs': null, 'spam': true})
var length = keys.length // access the `length` property as usual for arrays
var keys = new Array();
for(var key in obj)
{
keys[keys.length] = key;
}
var keyLength = keys.length;
to access any value from the object, you can use obj[key];
obj = {'a':'c','b':'d'}
You can try:
[index for (index in obj)]
this will return:
['a','b']
to get the list of keys
or
[obj[index] for (index in obj)]
to get the values
Anurags answer is basically correct.
But to support Object.keys(obj) in older browsers as well you can use the code below that is copied from
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
. It adds the Object.keys(obj) method if it's not available from the browser.
if (!Object.keys) {
Object.keys = (function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
Use Object.keys()... it's the way to go.
Full documentation is available on the MDN site linked below:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Note that in coffeescript this can be accomplished in all browsers and node as
k for k of obj
and thus
(1 for _ of obj).length
Recursive solution for browsers that support ECMAScript 5:
var getObjectKeys = function(obj) {
var keys = Object.keys(obj);
var length = keys.length;
if (length !== 0) {
for (var i = 0; i < length; i++) {
if (typeof obj[keys[i]] === 'object') {
keys[keys[i]] = getObjectKeys(obj[keys[i]]);
}
}
}
return keys;
};
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
}
console.log(Object.keys(obj));
console.log(Object.keys(obj).length)
If you decide to use Underscore.js you better do
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
}
var keys = [];
_.each( obj, function( val, key ) {
keys.push(key);
});
console.log(keys.lenth, keys);
In JavaScript, an object is a standalone entity, with properties and type.
For fetching values from Object in form of array:
Object.values(obj) // obj is object name that you used
Result -> ["value1", "value2", "value3", "value4"]
For fetching keys from Object in form of array:
Object.keys(obj) // obj is object name that you used
Result -> ["key1", "key2", "key3", "key4"]
As both functions are returning array you can get the length of keys or value by using length property. For instance - Object.values(obj).length or Object.keys(obj).length
Modern browsers do support:
var obj = {
key1: 'value1',
key2: 'value2',
key3: 'value3',
key4: 'value4'
}
console.log(Object.keys(obj));
// we can also get values
console.log(Object.values(obj));
For a comma-delineated string listing the keys of a JSON Object, try the following:
function listKeys(jObj){
var keyString = '';
for(var k in jObj){
keyString+=(','+k);
}
return keyString.slice(1);
}
/* listKeys({'a' : 'foo', 'b' : 'foo', 'c' : 'foo'}) -> 'a,b,c' */
Using ES6,
you can use forEach to iterate over the Keys of an Object.
To get all the keys you can use Object.keys which returns all the keys in an Object
Object.keys(obj).forEach(function(keyValue, index, map) {
console.log(keyValue);
});
Short hand of the above snippet would be, which only takes one parameter
Object.keys(obj).forEach(function(keyValue) {
console.log(keyValue);
});
if(props.userType){
var data = []
Object.keys(props.userType).map(i=>{
data.push(props.userType[i])
})
setService(data)
}
using slice, apply and join method.
var print = Array.prototype.slice.apply( obj );
alert('length='+print.length+' list'+print.join());
Here is solution for getting all the keys from an nested object/array.
It will recursively check for the object inside an array.
function Keys() {
let keys = [];
this.pushKey = function (key) {
keys.push(key);
};
this.getKeys = function () {
return keys;
};
}
let keys = new Keys();
let arr = [
{
a: 1,
b: {
c: [{ d: 1, e: [{ f: 1 }] }],
},
},
{
g: 1,
h: {
i: [{ j: 1, k: [{ l: 1 }] }],
},
},
];
function getObject(arr) {
for (let item of arr) {
if (Array.isArray(item)) getObject(item);
else getKeys(item);
}
}
function getKeys(obj) {
for (let key in obj) {
if (Array.isArray(obj[key])) getObject(obj[key]);
else if (typeof obj[key] === "object") getKeys(obj[key]);
keys.pushKey(key);
}
}
getObject(arr);
console.log(keys.getKeys());

Categories