i'm doing a project in VueJS and i have an array of data, which consists of a number of objects.
These objects are pulled from a PHP Backend and consist of values like
id: 2123
name: "Name Value"
status: "active"
account_id: "2KGGALS2353255"
Imagine i want to split these by the keys names into a similar array but i want to have a parent object that consists of two child objects
[
0: {
core: {
id: 2123
name: "Name Value"
},
extra: {
status: "active",
account_id: "2KGGALS2353255"
}
]
The question is how can i achieve this with Javascript? I don't really want to modify the data in PHP beforehand unless this is something very unadvised to do in Javascript.
I can use VueJS and Lodash.
I was looking for lodash's pick() method.
https://lodash.com/docs/4.17.4#pick
This should work for your purpose
function separate(obj, keys) {
let target = {}, rest = {};
Object.keys(obj).forEach(function(key) {
if (keys.includes(key)) {
target[key] = obj[key];
} else {
rest[key] = obj[key];
}
});
return { target: target, rest: rest };
}
let stuff = {
id: 2123,
name: "Name Value",
status: "active",
account_id: "2KGGALS2353255"
};
let separated = separate(stuff, ['id', 'name']);
console.log({
core: separated.target,
extra: separated.rest
});
Using ES6's object destructuring, and the object rest spread proposal, which requires a babel transform, you can Array#map the array into a new array of objects in the required format:
const arr = [{"id":1,"name":"Name1","status":"active","account_id":"2KGGALS2353255"},{"id":2,"name":"Name2","status":"active","account_id":"4ABCLS2353255"},{"id":3,"name":"Name3","status":"active","account_id":"6LMNALS2353255"}];
const result = arr.map(({ id, name, ...extra }) => ({
core: {
id,
name
},
extra
}));
console.log(result);
You can do the same thing using lodash's _.pick() to the get the core, and _.omit() to get the extra:
var arr = [{"id":1,"name":"Name1","status":"active","account_id":"2KGGALS2353255"},{"id":2,"name":"Name2","status":"active","account_id":"4ABCLS2353255"},{"id":3,"name":"Name3","status":"active","account_id":"6LMNALS2353255"}];
var result = arr.map(function(obj) {
return {
core: _.pick(obj, ['id', 'name']),
extra: _.omit(obj, ['id', 'name'])
};
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Related
I am having an array like this :
arr = [ {id:0,name:Mark} , {id:1,name:Ron}, {id:2,name:Henry}, {id:3,name:Rose}].
I want to create an object like this :
obj1 = { Mark:false, Ron:false, Henry:false, Rose:flase }
I am using map to traverse through the array like this
let obj1 = {};
obj1 = arr.map((item)=> {
obj1[item.name] = false;
})
How can I achieve the following result?
You could map entries and build an object from the pairs.
const
data = [{ id: 0, name: 'Mark' }, { id: 1, name: 'Ron' }, { id: 2, name: 'Henry' }, { id: 3, name: 'Rose' }],
result = Object.fromEntries(data.map(({ name }) => [name, false]));
console.log(result);
Object.fromEntries() is probably the best idea. But you could also use reduce, if you got more operations on the array and want to stick to the "pipe" approach.
const arr = [
{ id: 0, name: 'Mark' },
{ id: 1, name: 'Ron' },
{ id: 2, name: 'Henry' },
{ id: 3, name: 'Rose' }
];
const objA = arr
.reduce((previous, { name }) => ({ ...previous, [name]: false }), {});
const objB = arr
.reduce((previous, { name }) => {
previous[name] = false;
return previous;
}, {});
The spreach operation {...obj} for objA does effectivly copy the object on each extension, which might not be desirable. But the modern JavaScript engines simplify those expressions anyways.
objB is the more standard approach for me. One additional benefit, in regards to Object.fromEntries() is that you can have some form of standard or default object / settings, which you can use as the start of the reduce (the second parameter) and evaluate in the collection function.
All three options are valid and depend on your code style.
I need to merge two objects(obj1, obj2), that happen to share similar keys.
obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
}
obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
}
Expected result:
obj3 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"},
3:{"Example2": "Example2"},
4:{"Example2": "Example2"},
5:{"Example2": "Example2"},
}
Usual approach when merging two objects:
const obj3 = Object.assign({}, obj1, obj2);
Problem: They do share many keys, as such, in obj3, only the contents of obj2 are going to be found.
My approach:
let obj3= Object.assign({}, obj1);
for(let i=0; i<obj2.length; i++) {
obj3[obj3.length + i] = obj2[i];
}
Question: Is there, another more elegant, pre-defined way of merging two objects with similar keys?
Although I still think obj1 and obj2 should be arrays...
const obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
};
const obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
}
const result = Object.fromEntries(
Object.values(obj1) // get the values of the first object
.concat(Object.values(obj2)) // get the values of the second object and add them to the values of the first
.map((value, index) => [ index, value ]) // re-index them
// or if you need actual copies of the "inner" objects
/*
.map((value, index) => [
index,
Object.assign({}, value)
])
*/
);
console.log(result);
Is this "more elegant". Maybe...
The objects in your code are key-value pairs rather than a simple list (array).
From the looks of it there are only two possible scenarios:
Your objects have a meaningful, unique key associated to them (for example, this very question on stackoverflow has key 69316153 – look at the URL bar). In this case, you really can't merge the two as they have conflicting keys. Think if there was another question on this website with the same ID as this one!
The keys are not meaningful and you're happy with the same object being re-assigned a different key. In this case the correct data structure to use is arrays (obj1 = [{"Example": "..."}, {"Example": "..."}]).
If the latter is your situation, this code will work:
const obj3 = Object.values(obj1).concat(Object.values(obj2))
Object.values(obj) returns an array of values, discarding all of the keys.
Let's say we have:
const obj1 = {
1: { Name: "Apple" },
2: { Name: "Watermelon" },
};
const obj2 = {
2: { Name: "Pear" },
5: { Name: "Tomato" }
};
Object.values(obj1) will return [{ Name: "Apple" }, { Name: "Watermelon" }], while Object.values(obj2) will return [{ Name: "Pear" }, { Name: "Tomato" }].
With const obj3 = Object.values(obj1).concat(Object.values(obj2)) you will end up with:
obj3 = [
{ Name: "Apple" },
{ Name: "Watermelon" },
{ Name: "Pear" },
{ Name: "Tomato" }
];
Because you have key-value maps and not arrays, you can't just combine the objects. Your only way would be to iterate through all the keys and add them to the final result.
E.g. something along the lines:
const obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
};
const obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
};
function merge(...args) {
return Object.fromEntries( // Create entries
args // From all arguments
.flatMap(o => Object.values(o)) // Get values (discard keys)
.map((element, index) => [ index, element ]) // Remap them
);
}
console.log(merge(obj1, obj2));
// Will output
// {
// 0: { Example1: "Example1" },
// 1: { Example1: "Example1" },
// 2: { Example1: "Example1" },
// 3: { Example2: "Example2" },
// 4: { Example2: "Example2" },
// 5: { Example2: "Example2" }
// }
I want to filter items from the categories array based on the criteria in the otherCategories array.
If otherCategories contains an object where title matches one title from categories.subCategory[i].title and name matches categories.subCategory[i].details.name, then filter only that object e.g "item1" from categories.
var categories = [
{
title:"item1",
subCategory:[
{
title:"subCat1",
details:{
name:"detail1",
email:"test#test.com"
}
},
{
title:"subCat2",
details:{
name:"detail2",
email:"test#test.com"
}
}
]
},
{
title:"item2",
subCategory:[
{
title:"subCat1",
details:{
name:"detail3",
email:"test#test.com"
}
},
{
title:"subCat2",
details:{
name:"detail2",
email:"test#test.com"
}
}
]
}
]
var otherCategories = [
{
title:"subCat1",
name:"detail1"
}
]
Expected result
categories = [
{
title:"item1",
subCategory:[
{
title:"subCat1",
details:{
name:"detail1",
email:"test#test.com"
}
},
{
title:"subCat2",
details:{
name:"detail2",
email:"test#test.com"
}
}
]
}]
Use Array.reduce, Array.filter & Array.some
Convert the otherCategories array to an object with title as key and name as value
Filter categories array where some subCategory exists with matching values
var categories = [{title:"item1",subCategory:[{title:"subCat1",details:{name:"detail1",email:"test#test.com"}},{title:"subCat2",details:{name:"detail2",email:"test#test.com"}}]},{title:"item2",subCategory:[{title:"subCat1",details:{name:"detail3",email:"test#test.com"}},{title:"subCat2",details:{name:"detail2",email:"test#test.com"}}]}];
var otherCategories = [{title:"subCat1",name:"detail1"}];
var obj = otherCategories.reduce((a,c) => Object.assign(a,{[c.title]:c.name}), {});
categories = categories.filter(v => v.subCategory.some(o => obj[o.title] === o.details.name));
console.log(categories);
You could map the categories to the results by filtering the subCategories:
function matches(sub, filters) {
return filters.some(filter => filter.title === sub.title && filter.name === sub.name);
}
const result = categories.map(({ title, subCategories }) => ({ title, subCategories: subCategories.filter(sub => matches(sub, otherCategories)) }));
Another approach that will usually work for simple objects like the ones in your example, is to convert your otherCategories array to an array of "stringified" objects, and then filter categories by comparing "stringified" versions of the desired subCategory key value pairs to the converted otherCategories array.
Important to note, however, that object property order is not guaranteed in JavaScript (although many browsers will preserve property order). That means that this approach may not work in some situations and an approach like the one suggested by #NikhilAggarwal is more stable.
For example:
const categories = [{title: "item1", subCategory: [{title: "subCat1", details: {name: "detail1", email: "test#test.com"}},{title: "subCat2", details: {name: "detail2", email: "test#test.com"}}]}, {title: "item2", subCategory: [{title: "subCat1", details: {name: "detail3", email: "test#test.com"}},{title: "subCat2", details: {name: "detail2", email: "test#test.com"}}]}];
const otherCategories = [{title: "subCat1", name: "detail1"}];
const matches = otherCategories.map((item) => JSON.stringify(item));
const results = categories.filter((item) => {
for (const sub of item.subCategory) {
const match = JSON.stringify({title: sub.title, name: sub.details.name});
if (matches.includes(match)) {
return true;
}
return false;
}
});
console.log(results);
I have an array
var keys = ['Name','Id'];
which I want to merge with the dictionary object below
var projects = {
"project1": "11111",
"project2": "22222",
"project3": "33333",
};
to produce the output below
output =
[
{ Name:"project1", Id:"11111"},
{ Name:"project2", Id:"22222"},
{ Name:"project3", Id:"33333"},
]
I have tried using
console.log(_.zipObject(keys, projects));
but this fails woefully
How do I do this using lodash?
Since you have asked to use lodash specifically, you can use _.map.
DEMO
var projects = {
"project1": "11111",
"project2": "22222",
"project3": "33333",
};
var result = _.map(projects, function(value, prop) {
return { Name: prop, id: value };
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
Use Object.keys() with Array.map() to create the array of objects. You can assign the key names by using computed property names:
var keys = ['Name','Id'];
var projects = {
"project1": "11111",
"project2": "22222",
"project3": "33333",
};
var result = Object.keys(projects)
.map(function(k) {
return {
[keys[0]]: k,
[keys[1]]: projects[k]
};
});
console.log(result);
I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case