Finding matching objects in an array of objects? - javascript

var set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
I'd like to be able to do something like a db call, set.find({"color":"green"}) and have it return an array full of objects that contain that property.

Using Array#filter, for this particular case the code would look like
var results = set.filter(function (entry) { return entry.color === "green"; });
Array#filter is not implemented in some older browsers, so see the linked article for a backward compatibility shim, or better yet get a full-fledged ES5 shim.
For the more general case, it's just a matter of extending this idea:
function findByMatchingProperties(set, properties) {
return set.filter(function (entry) {
return Object.keys(properties).every(function (key) {
return entry[key] === properties[key];
});
});
}
var results = findByMatchingProperties(set, { color: "green" });
Again, I am using ECMAScript 5 methods Object.keys and Array#every, so use an ES5 shim. (The code is doable without an ES5 shim but uses manual loops and is much less fun to write and read.)

I have used map function from jquery and I am getting selected index by passing searched key value so by using that index we will get required object from array.
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
output
{ name: "Shyam", Id: 2 }
Note: if you want to pass search key as object then
searchKey = { Id: 2 };
mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey.Id)];
output
{ name: "Shyam", Id: 2 }

Using arrow functions with an implied return and concise body:
const results = set.filter(entry => entry.color === "green");
Another example passing in a search variable:
const searchString = 'green';
const results = set.filter(entry => entry.color === `${searchString}`);
Read more about arrow functions on
MDN

Since you've included the jQuery tag, here's one way to do it using jQuery's map:
var results = $.map( set, function(e,i){
if( e.color === 'green' ) return e;
});
The documentation states that you need to return null to remove the element from the array, but apparently this is false, as shown by the jsFiddle in the comments; returning nothing (i.e. returning undefined) works just as well.

I went with a different approach that I found to be a bit easier.
function isObjEqual(a, b) {
const x = JSON.stringify(a);
const y = JSON.stringify(b);
return x === y;
}
// Example 1
const set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
const findObj1 = {"color":"green"};
const arr1 = set.filter((objInArr) => isObjEqual(objInArr, findObj1));
console.log(arr1) // [ { color: 'green' }, { color: 'green' } ]
// Example 2
const list = [{
"label": "Option 2",
"value": "option2"
},
{
"label": "Option 3",
"value": "option3"
},
{
"label": "Option 2",
"value": "option2"
}
];
const findObj2 = {
"label": "Option 2",
"value": "option2"
}
const newList = list.filter((objInArr) => isObjEqual(objInArr, findObj2));
console.log(newList) //[ { label: 'Option 2', value: 'option2' }, { label: 'Option 2', value: 'option2' } ]

Related

removing object from array with filters doesnt work in javascript

I am trying to remove an object from an array but it I dont know what I am doing wrong.
I have this array declared:
listA: [
{ title: 'Food', value: 'Patato' },
{ title: 'Drink', value: 'Cola' },
{ title: 'Desert', value: 'Cheesecake' },
],
I am trying to remove the object where its value is 'Cola', what I have tried is this:
this.listA.filter(x => x.value !== 'Cola');
And it returns me the same list
I want to return this:
listA: [
{ title: 'Food', value: 'Patato' },
{ title: 'Desert', value: 'Cheesecake' },
],
Your code should be filtering just fine, I think the issue here is that filter does not modify the original array, it returns a new array with the filtered results. If you want it to overwrite the original array, you'll need to say this.listA = this.listA.filter(...)
const listA = [
{ title: "Food", value: "Patato" },
{ title: "Drink", value: "Cola" },
{ title: "Desert", value: "Cheesecake" },
];
const result = listA.filter((obj) => obj.value !== 'Cola');
Looks like you need to do something like
this.listA = this.listA.filter(x => x.value !== 'Cola')
The filter method is immutable hence the original array isn't changed
As a complement to https://stackoverflow.com/a/70688107/6316468, here is what filter does under the hood (the original array this remains untouched):
var xs = [1, 2, 3, 4];
var ys = filter.call(
xs, x => x % 2
);
console.log(
"xs = [", xs.join(), "]"
);
console.log(
"ys = [", ys.join(), "]"
);
function filter(predicate) {
var xs = []; // new array
for (let x in this) {
if (predicate(x)) {
xs.push(x);
}
}
return xs;
}

Comparing two array of objects and replacing an object with another

I have a working function that appends an array of objects customData to the end of another array of objects testData. If an object with the same property name value appears in both arrays, then the testData object is removed and replaced with the customData object in the resulting array. The customData object takes on the order of the previous testData object.
This is my attempt however, I'm wondering if there is a better way of doing this which is also easy to read (es6)?
Thanks
https://codesandbox.io/s/recursing-river-bdp5k?file=/src/App.js
export default function App() {
const testData = [
{ display: "1", name: "TEST1" },
{ display: "2", name: "TEST2" }
];
const customData = [
{ display: "CUSTOM_1", name: "TEST1", custom: "YES" },
{ display: "CUSTOM_3", name: "TEST3", custom: "YES" }
];
let i = 0;
const newTestData = testData;
let newCustomData = customData;
while (i < customData.length) {
const view = customData[i];
const index = testData.findIndex(x => x.name === view.name);
if (index >= 0) {
newTestData[index] = customData[i];
newCustomData.splice(i, 1);
}
i += 1;
}
const concatData = newTestData.concat(newCustomData);
console.log(concatData)
return null;
}
Array#concat does not mutate the arrays, there's no need to assign them to new variables (which doesn't copy the arrays anyway). If you're seeking to use concise ES6 code, skip the while loop - there are numerous equivalents. Here's one example:
You don't define "better way" but I'll interpret it to mean "most optimized for performance and readability". In the below approach I use one pass to populate a map, and another pass to overwrite the map entries with customData where needed, and finally a Object.values() (technically a third pass) to produce the results. This is O(n) (no nested loops) versus your O(n^2) implementation.
const testData = [
{ display: "1", name: "TEST1" },
{ display: "2", name: "TEST2" }
];
const customData = [
{ display: "CUSTOM_1", name: "TEST1", custom: "YES" },
{ display: "CUSTOM_3", name: "TEST3", custom: "YES" }
];
let map = {};
testData.forEach(item => map[item.name] = item);
customData.forEach(item => map[item.name] = item);
const result = Object.values(map);
console.log(result);
For many cases including this one, where you have a bunch of data identified by a unique key (such as name), you really ought to be using objects to begin with. Any time you need an array, there's Object.values() and Object.keys().
Logic behind your code is correct.
This is almost the same, but without explicit loops:
const testData = [
{ display: "1", name: "TEST1" },
{ display: "2", name: "TEST2" }
];
const customData = [
{ display: "CUSTOM_1", name: "TEST1", custom: "YES" },
{ display: "CUSTOM_3", name: "TEST3", custom: "YES" }
];
Array.prototype.uniqueWith = function(comparator) {
return this.reduce((a, c, i) => {
const j = a.slice(i+1).findIndex(e => comparator(e, c));
if(j !== -1) {
a[i] = a[i+j+1];
a.splice(i+j+1, 1);
}
return a;
}, this);
}
const eqByName = (a, b) => a.name === b.name;
const result = [...testData, ...customData].uniqueWith(eqByName);
console.log(result);
Take a note that extending Array prototype may not be the best idea, so you may create separate function, which will take concatenated array as an argument.
Is this the kind of function you're looking for?
const result = App(myTestData(), myCustData());
console.log(result);
function App(testData, custData) {
// `indByName` returns index (in arr) of obj w/ matching name (or -1 if no match)
const indByName = (arr, name) =>
ind = arr.findIndex(o => o.name === name);
let custInd; // Identifier for output of `indByName`
const custDataClone = custData.slice(); // Prevents mutating `custData`
return testData.map(item => (
// Uses indByName to get index of corresponding custom item
custInd = indByName(custDataClone, item.name),
// Moves corresponding custom item into testData if appropriate
custInd > -1 ? custDataClone.splice(custInd, 1)[0] : item
// Appends remaining items from custDataClone to the new array
)).concat(custDataClone)
}
function myTestData(){
return [
{ display: "1", name: "TEST1" },
{ display: "2", name: "TEST2" }
];
}
function myCustData(){
return [
{ display: "CUSTOM_1", name: "TEST1", custom: "YES" },
{ display: "CUSTOM_3", name: "TEST3", custom: "YES" }
];
}

Why map() operation on a javascript array to copy it to another array after changing an element returning undefined?

let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
I have this array, I want to copy it to another array after changing one element, but my code does not work out.
array_copy = array.map((element) => {
if (element.id === 2) {
element.name = "name changed";
}
});
console.log(array_copy);
I am getting this output--
(3) [undefined, undefined, undefined]
I am new to js and self learning it, can u pls help me understand this?
Here is the link of the question
Copy javaScript array by changing one element
--I tried to answer . What is wrong with my logic/approach?
You need to return something from the function you are using for the map-callback
Do this instead:
array_copy = array.map((element) => {
console.log(element.id);
if (element.id === 2) {
element.name = "name changed";
}
return element
});
As you are not returning anything from the function, you are getting undefined for each iteration
If you don't want to change your source array you have to copy element from the source array and return the copied and modified elements instead.
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
let array_copy = array.map((element) => {
let copy_element = Object.assign({}, element)
if (copy_element.id === 2) {
copy_element.name = "name changed";
}
return copy_element ;
});
console.log('array_copy', array_copy);
console.log('array',array);
You are missing the return statement, thats why you getting undefined.
this is the correct code
array_copy = array.map((element) => {
console.log(element.id);
if (element.id === 2) {
element.name = "name changed";
}
return element;
});
console.log(array_copy);
You need to add the return statement otherwise it will be undefined as expected but Array map changes the main array elements so log the main array and see you'll get modified it also.
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
array_copy = array.map((element) => {
if (element.id === 2) {
element.name = "name changed";
}
return element;
});
console.log(array,array_copy);

Create new array from iterating JSON objects and getting only 1 of its inner array

See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/

How can I get a unique array based on object property using underscore

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

Categories

Resources