I have an object with simple key value pairs and an array with just key names. I would like to sort the objects keys by the order of the keys in the array.
A simplified example:
const obj = {
"bread": 11,
"butter": 6,
"milk": 40,
}
const orderedKeys = ["milk", "butter", "bread"]
const expectedResult = {
"milk": 40,
"butter": 6,
"bread": 11,
}
In this example I know the amount of keys in both the array and the object, however in reality I don't know the amount and in reality the object can also have more keys than specified in the array. In this case I just want the unknown keys at the end of the object.
What would be the cleanest way to solve this problem?
You can take advantage of Array.prototype.reduce which allow to perform different manipulation on array and return another kind of object or array
const obj = {
"bread": 11,
"butter": 6,
"milk": 40,
}
const orderedKeys = ["milk", "butter", "bread"]
const expectedResult = orderedKeys.reduce((accumulator, current) => {
return {...accumulator, [current]: obj[current]};
}, {})
console.log(expectedResult);
If you want to add key which aren't present in the orderedKey but are present inside of the obj you can perform it like this
const obj = {
"bread": 11,
"butter": 6,
"milk": 40,
"cheese": 5
}
const orderedKeys = ["milk", "butter", "bread"]
let expectedResult = orderedKeys.reduce((accumulator, current) => {
return {...accumulator, [current]: obj[current]};
}, {});
expectedResult = Object.assign({}, expectedResult, obj);
console.log(expectedResult);
The simplest way is probably this:
map the ordered keys to [key, value] tuples
Reconstruct the object from those using Object.fromEntries
...spread the remaining object properties
const obj = {
unknown: 15,
bread: 11,
butter: 6,
milk: 40,
}
const orderedKeys = ['milk', 'butter', 'bread']
const orderedObj = {
...Object.fromEntries(orderedKeys.map(k => [k, obj[k]])),
...obj,
}
console.log(orderedObj)
However, relying on the order of keys in an object is generally a bad idea, because JavaScript objects are typically considered to be unordered ({ a: 1, b: 2 } is usually considered equivalent to { b: 2, a: 1 }). Whether or not they're ordered in practice depends somewhat on implementation. For this reason, and depending on your use case, you could consider using a Map instead:
new Map(orderedObj)
I need to convert an array of objects into an object of objects properties from the array.
Here is an example of an array of objects
const array = [
{
book:5,
car: 6,
pc: 7
},
{
headphone: 9,
keyboard: 10
},
];
I need it to be converted to
const obj = {
book:5,
car: 6,
pc: 7,
headphone: 9,
keyboard: 10
};
I tried many ways but can't achieve the final result. Thanks in advance
You could spread the array as parameters (spread syntax ...) for Object.assign, which returns a single object.
const
array = [{ book: 5, car: 6, pc: 7 }, { headphone: 9, keyboard: 10 }],
object = Object.assign({}, ...array);
console.log(object);
You can use .reduce() and Object.assign() methods:
const array = [
{book:5, car: 6, pc: 7},
{headphone: 9, keyboard: 10},
];
const result = array.reduce((r, c) => Object.assign(r, c), {});
console.log(result);
You can also loop through the array using for loops. Using .reduce() and Object.assign() may not me that clear to understang what is happening for people that don't understand too much about Objects in js, but is definitively less code.
for(let i = 0; i < array.length; i++){
for (let key in array[i]) {
if (array[i].hasOwnProperty(key)) {
obj[key] = array[i][key];
}
}
}
How about
let obj = {}
for(let object of array) {
Object.assign(obj, object)
}
console.log(obj)
I'm looking to append keys to the array which I have created, so I have an array of numbers:
var Array1 = [1, 2, 3, 4, 5];
I want to convert the array so it looks like this:
Array1 = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }];
How do I append to each value in the array like so?
https://www.sitepoint.com/a-beginners-guide-to-data-binding-in-d3-js/ - Within this article, we can see they have an array called 'Data' - An array of objects. Now I have a simple array full of numbers and I need it to be converted as described above
You could use Array#map together with short hand properties for a new object for each element of the array.
var array = [1, 2, 3, 4, 5],
objects = array.map(x => ({ x }));
console.log(objects);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array1 = Array1.map(entry => ({x: entry}));
I have array of object and I want to change into array an remove object.
My object is like:
[{ABC: "ELEMENT1", MAX: 2, MIN: "Yes"}, {ABC: "ELEMENT2", MAX: 1, MIN: "Yes"}]
and I want result like array with index:
[{"ELEMENT1",2,"Yes"},{"ELEMENT2",2,"Yes}]
Use Array#map over Object.keys(obj)
The Object.keys() method returns an array of a given object's own enumerable properties.
The map() method creates a new array with the results of calling a provided function on every element in this array.
var ip = {
STU: "Study1",
SUB: 2,
EXL: "Yes"
};
var op = Object.keys(ip).map(function(key) {
return ip[key];
});
console.log(op);
To iterate Array-of-Objects
var ip = [{
STU: "Study1",
SUB: 2,
EXL: "Yes"
}, {
STU: "Study2",
SUB: 4,
EXL: "No"
}];
var op = ip.map(function(item) {
return Object.keys(item).map(function(key) {
return item[key];
});
});
console.log(op);
That would be:
var array = Object.keys(obj).map(key => obj[key]);
However, order is not guaranteed for Object.keys as it works like a for-in loop, whose order of traversal is arbitrary. You may not get ["Study1",2,"Yes"] in that exact order. If you want to guarantee order, you must use an array containing the order of the keys and extract them from your object in that order.
var keyArray = ['STU', 'SUB', 'EXL'];
var array = keyArray.map(key => obj[key]);
I have an array of objects
list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}]
And I'm looking for an efficient way (if possible O(log(n))) to remove duplicates and to end up with
list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}]
I've tried _.uniq or even _.contains but couldn't find a satisfying solution.
Thanks!
Edit : The question has been identified as a duplicate of another one. I saw this question before posting but it didn't answer my question since it's an array of object (and not a 2-dim array, thanks Aaron), or at least the solutions on the other question weren't working in my case.
Plain javascript (ES2015), using Set
const list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }];
const uniq = new Set(list.map(e => JSON.stringify(e)));
const res = Array.from(uniq).map(e => JSON.parse(e));
document.write(JSON.stringify(res));
Try using the following:
list = list.filter((elem, index, self) => self.findIndex(
(t) => {return (t.x === elem.x && t.y === elem.y)}) === index)
Vanilla JS version:
const list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];
function dedupe(arr) {
return arr.reduce(function(p, c) {
// create an identifying id from the object values
var id = [c.x, c.y].join('|');
// if the id is not found in the temp array
// add the object to the output array
// and add the key to the temp array
if (p.temp.indexOf(id) === -1) {
p.out.push(c);
p.temp.push(id);
}
return p;
// return the deduped array
}, {
temp: [],
out: []
}).out;
}
console.log(dedupe(list));
I would use a combination of Arrayr.prototype.reduce and Arrayr.prototype.some methods with spread operator.
1. Explicit solution. Based on complete knowledge of the array object contains.
list = list.reduce((r, i) =>
!r.some(j => i.x === j.x && i.y === j.y) ? [...r, i] : r
, [])
Here we have strict limitation on compared objects structure: {x: N, y: M}. And [{x:1, y:2}, {x:1, y:2, z:3}] will be filtered to [{x:1, y:2}].
2. Generic solution, JSON.stringify(). The compared objects could have any number of any properties.
list = list.reduce((r, i) =>
!r.some(j => JSON.stringify(i) === JSON.stringify(j)) ? [...r, i] : r
, [])
This approach has a limitation on properties order, so [{x:1, y:2}, {y:2, x:1}] won't be filtered.
3. Generic solution, Object.keys(). The order doesn't matter.
list = list.reduce((r, i) =>
!r.some(j => !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])
This approach has another limitation: compared objects must have the same list of keys.
So [{x:1, y:2}, {x:1}] would be filtered despite the obvious difference.
4. Generic solution, Object.keys() + .length.
list = list.reduce((r, i) =>
!r.some(j => Object.keys(i).length === Object.keys(j).length
&& !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])
With the last approach objects are being compared by the number of keys, by keys itself and by key values.
I created a Plunker to play with it.
One liners for ES6+
If you want to find uniq by x and y:
arr.filter((v,i,a)=>a.findIndex(t=>(t.x === v.x && t.y===v.y))===i)
If you want to find uniques by all properties:
arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)
The following will work:
var a = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];
var b = _.uniq(a, function(v) {
return v.x && v.y;
})
console.log(b); // [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 } ]
Filter the array after checking if already in a temorary object in O(n).
var list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }],
filtered = function (array) {
var o = {};
return array.filter(function (a) {
var k = a.x + '|' + a.y;
if (!o[k]) {
o[k] = true;
return true;
}
});
}(list);
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
No libraries, and works with any depth
Limitation:
You must provide only string or Number properties as hash objects otherwise you'll get inconsistent results
/**
* Implementation, you can convert this function to the prototype pattern to allow
* usage like `myArray.unique(...)`
*/
function unique(array, f) {
return Object.values(
array.reduce((acc, item) => ({ ...acc, [f(item).join(``)]: item }), {})
);
}
const list = [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}, { x: 1, y: 2}];
// Usage
const result = unique(list, item => [item.x, item.y]);
// Output: [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}]
console.log(result);
Snippet Sample
// Implementation
function unique(array, f) {
return Object.values(
array.reduce((acc, item) => ({ ...acc, [f(item).join(``)]: item }), {})
);
}
// Your object list
const list = [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}, { x: 1, y: 2}];
// Usage
const result = unique(list, item => [item.x, item.y]);
// Add result to DOM
document.querySelector(`p`).textContent = JSON.stringify(result, null, 2);
<p></p>
With Underscore's _.uniq and the standard JSON.stringify it is a oneliner:
var list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];
var deduped = _.uniq(list, JSON.stringify);
console.log(deduped);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
However, this presumes that the keys are always specified in the same order. By sophisticating the iteratee, we can make the solution work even if the order of the keys varies. This problem as well as the solution also apply to other answers that involve JSON.stringify.
var list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {y:2, x:1}];
// Ensure that objects are always stringified
// with the keys in alphabetical order.
function replacer(key, value) {
if (!_.isObject(value)) return value;
var sortedKeys = _.keys(value).sort();
return _.pick(value, sortedKeys);
}
// Create a modified JSON.stringify that always
// uses the above replacer.
var stringify = _.partial(JSON.stringify, _, replacer, null);
var deduped = _.uniq(list, stringify);
console.log(deduped);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
For Lodash 4, use _.uniqBy instead of _.uniq.
Using lodash you can use this one-liner:
_.uniqBy(list, e => { return e.x && e.y })