Updating selection of array's values - javascript

I have two arrays. Each array could have a different number of objects but they each have the same properties but could have different values. For example
var Array1 = [ { id: '1', value: a },
{ id: '2', value: b } ]
var Array2 = [ { id: '', value: c },
{ id: '', value: d },
{ id: '', value: a } ]
What I want
AfterArray = [ { id: '1', value: a },
{ id: '3', value: c },
{ id: '4', value: d } ]
What's happening is that array1's object will be removed if it doesn't have array2's value. If it does have array2's value, it will keep the original id. If an object is in array2 that isn't in array1, an id will be generated (UUID).
I'm assuming it might go something like this
afterArray = []
this.Array1.forEach((res, i) => {
this.Array2.forEach((res2, 2) => {
if(res.value == res2.value){
afterArray = afterArray.concat(this.Array1[i])
}
else {
// do something if values are not present then add to array.
// if added, add id to those empty properties.
}
})
})
Thanks!

You just need a simple mapping over Array2 with a find inside it, to find the matching value in Array1 if it exists:
const array1 = [
{
id: '1',
value: 'a'
},
{
id: '2',
value: 'b'
}
];
const array2 = [
{
id: '',
value: 'c'
},
{
id: '',
value: 'd'
},
{
id: '',
value: 'a'
}
];
const generateId = (() => {
// example generator function, use your own instead
let possibleIds = ['3', '4'];
let i = -1;
return () => {
i++;
return possibleIds[i];
};
})();
const result = array2.map(({ id, value }) => {
// find a matching value in array1 to merge the id:
const foundArr1Item = array1.find(({ value: ar1Val }) => ar1Val === value);
// otherwise, generate a new ID:
if (foundArr1Item) return { value, id: foundArr1Item.id };
return { value, id: generateId() };
});
console.log(result);

If I understood it right, this should do your job:
(find the comments in the code)
Array1 = [
{
id: '1',
value: "a"
},
{
id: '2',
value: "b"
}
]
Array2 = [
{
id: '',
value: "c"
},
{
id: '',
value: "d"
},
{
id: '',
value: "a"
}
]
// keep Array1's objects if it has a value matching a value from any Array2 object
// Also remove those objects from Array2
newArray1 = Array1.reduce((acc, elem) => {
let indexOfObInArray2 = Array2.findIndex(eachArray2Elem => {
return elem.value == eachArray2Elem.value
});
if (indexOfObInArray2 > -1) {
acc.push(elem);
Array2.splice(indexOfObInArray2, 1);
}
return acc;
}, [])
// Array of ids already taken by Objects from Array2, if they are non empty
idsTakenInArray2 = Array2.reduce((acc, x) => {
if (x.id != "") {
acc.push(x.id);
}
return acc;
}, []);
// random number to give ids
randomId = 1;
Array2 = Array2.map(eachElem => {
if (eachElem.id == '') {
while (Array1.find(eachArray1Elem => {
return eachArray1Elem.id == randomId
}) || idsTakenInArray2.indexOf(randomId) !== -1) {
randomId++;
}
eachElem.id = randomId;
idsTakenInArray2.push(randomId);;
}
return eachElem;
})
console.log(newArray1.concat(Array2));

check this, here is the code online https://stackblitz.com/edit/angular-zhzuqk , check your console you will see what you want to have as result
formtarrays(array1,array2) {
let ar = array1.concat(array2);
// delete items that exist in array1 but not in array2
ar = ar.filter((elem) => {
return !(array1.findIndex(item => item.value === elem.value) !== -1 && array2.findIndex(item => item.value === elem.value) === -1)
})
// get distinct values
const idList = [];
const distinct = [];
ar.forEach((item, index) => {
if (item !== undefined) {
idList['id'] = item.value;
if (idList.indexOf(item.value) < 0) {
if(item.id === '') {
item.id = (index + array1.length).toString();
}
distinct.push(item);
idList.push(item.value);
}
}
})
console.log(distinct);
return distinct;
}

Related

Find the unique id of the item and group its value in array using reduce method?

Please help me to find the expected output from the given scenario
input array:
const items = [
{ id: 1, name: "a" },
{ id: 2, name: "b" },
{ id: 3, name: "c" },
{ id: 1, name: "d" },
{ id: 3, name: "f" },
{ id: 1, name: "a" },
{ id: 3, name: "c" },
]
expected output:
[{ id: 1, names: ['a', 'd']},
{ id: 2, names: ['b']},
{ id: 3, names: ['c', 'f']}]
You can create a new array, loop through your main array and check if there is an object with the current id in the new array and update it or create a new object accordingly.
Like this:
let newItems = [];
items.forEach(item => {
let index = newItems.findIndex(el => el.id == item.id);
if (index > -1) {
if (newItems[index]['names'].indexOf(item.name) === -1) {
return newItems[index]['names'].push(item.name)
}
} else {
newItems.push({id: item.id, names: [item.name]});
}
});
With reduce method:
const newArr = items.reduce((pv, cv) => {
let index = pv.findIndex(el => el.id == cv.id);
if (index > -1) {
if (pv[index]['names'].indexOf(cv.name) === -1) {
pv[index]['names'].push(cv.name)
}
} else {
pv.push({id: cv.id, names: [cv.name]});
}
return pv;
}, []);
pv is previous value which is the new array, cv is current value which is each object in items array. Initial value of newArr is []
You can use spread operator and retrieve the values of the duplicate key and push it in the new array of objects.
Thanks & Regards

Change position of array element

let cards = [
{ name: 'a', value: '234'},
{ name: 'b', value: '876' },
{ name: 'c', value: '765'},
{ name: 'd', value: '980' }
];
let defaultCard = 'c';
I want to the defaultCard to be the first card in cards array.
In this case result will be
cards = [
{ name: 'c', value: '765'},
{ name: 'a', value: '234'},
{ name: 'b', value: '876' },
{ name: 'd', value: '980' }
]
What is the best possible way.
What I have done is to get index of default card and swap with the 0 index.
function getIndex(arr, defaultCard) {
return arr.findIndex((item) => item.name === defaultCard);
}
function getSortedCard(arr) {
let index = getIndex(arr, defaultCard);
if (index < 1) return arr;
let temp = arr[index];
arr[index] = arr[0];
arr[0] = temp;
return arr;
}
const newArr = getSortedCard(cards);
console.log(newArr);
Here's a way to do it via destructuring and Array.filter method.
let cards = [
{ name: 'a', value: '234'},
{ name: 'b', value: '876' },
{ name: 'c', value: '765'},
{ name: 'd', value: '980' }
];
let defaultCard = 'c';
cards = [cards.find(item => item.name === defaultCard), ...cards.filter(item => item.name !== defaultCard)];
console.log(cards);
If you don't want to change the original array, you can find the index (Array.findIndex()), and then construct a new array by putting the element at the index at the top, and spreading the slices that come before and after the index:
const moveToTop = (name, arr) => {
const idx = arr.findIndex(o => o.name === name);
if(idx === -1) return arr;
return [
arr[idx],
...arr.slice(0, idx),
...arr.slice(idx + 1)
]
}
const cards = [{"name":"a","value":"234"},{"name":"b","value":"876"},{"name":"c","value":"765"},{"name":"d","value":"980"}];
const defaultCard = 'c';
const result = moveToTop(defaultCard, cards);
console.log(result);
If you want to mutate the array, use Array.splice() to remove the item, and use Array.unshift() to insert it at the top:
const moveToTop = (name, arr) => {
const idx = arr.findIndex(o => o.name === name);
if(idx === -1) return arr;
arr.unshift(...arr.splice(idx, 1));
return arr;
}
const cards = [{"name":"a","value":"234"},{"name":"b","value":"876"},{"name":"c","value":"765"},{"name":"d","value":"980"}];
const defaultCard = 'c';
const result = moveToTop(defaultCard, cards);
console.log(result);
You can use the Array.sort()method and have the match sort higher:
let cards = [{
name: 'a',
value: '234'
},
{
name: 'b',
value: '876'
},
{
name: 'c',
value: '765'
},
{
name: 'd',
value: '980'
}
];
let defaultCard = 'c';
let ans = cards.sort((a, b) => {
if (a.name === defaultCard) {
return -1
}
return 1
})
console.log(ans)

Javascript get full index of nested object

const items = [
{ id: 'item1',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3'
children: [
{ id: 'item1-1-3-1'}
]
},
]
},
{ id: 'item1-2',
children: [
{ id: 'item1-2-1' }
]
}
]
},
{ id: 'item2' }
]
What I want to is like below,
function getFullDepthOfObject(){
...
}
getFullIndexOfObject('item1') =====> return '1'
getFullIndexOfObject('item1-2') =====> return '1-2'
getFullIndexOfObject('item1-1-1') =====> return '1-1-1'
getFullIndexOfObject('item1-1-2') =====> return '1-1-2'
getFullIndexOfObject('item2') ===> return '2'
I have struggled with this too much time, But I couldn't make it. I think I should stack each of parent index, But I don't know how to get its parent. Is there a way to do this?
Not parse of id string. Each id has randomic string. The id like item1-2 is for easier demonstration.
I think my way is too verbose...
I tried like ...
// Get Full Index of item1-1
// First, get the target's depth.
var depth = 0;
function getDepthOfId(object, id) {
var level;
if (object.id === id) return 1;
object.children && object.children.some(o => level = getDepthOfId(o, id));
return level && level + 1;
}
depth = getDepthOfId(items[0], 'item1-1');
console.log('depth === ', depth)
// Then, iterate recursively with length of depth.
var indexStacks = [];
function getNestedIndexOfId(obj, id, index) {
if (obj.id === id) {
indexStacks = [index, ...indexStacks]
return index;
}
if (obj.children) {
depth++;
obj.children.map((child, i) => {
getNestedIndexOfId(child, id, i)
})
}
}
// I can get the inner index, but I can't get its parent id.
// I don't know how to this..
function getParentId(obj, id){
// ...?
var parentId;
return parentId;
}
for(var i=0; i<depth; i++){
getNestedIndexOfId('...')
}
// full path will be
indexStacks.join('-')
const items = [
{ id: 'item1',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3',
children: [
{ id: 'item1-1-3-1'}
]
}
]
},
{ id: 'item1-2',
children: [
{ id: 'item1-2-1' }
]
}
]
},
{ id: 'item2' }
];
const searchIt = (node, search, path = '', position = 0) => {
if (node.id && node.id === search) {return path !== '' ? `${path}-${position}` : position;}
if (!node.children) {return false}
const index = node.children.findIndex((x) => x.id && x.id === search);
if (index >= 0) {
return path !== '' ? `${path}-${index + 1}` : index + 1;
}
for (let i = 0; i < node.children.length; i++) {
const result = searchIt(node.children[i], search, path !== '' ? `${path}-${i+1}` : i + 1, i);
if (result){
return result;
}
}
return false;
};
console.log(searchIt({children: items}, 'item1-1'));
console.log(searchIt({children: items}, 'item1-1-1'));
console.log(searchIt({children: items}, 'item1-1-2'));
console.log(searchIt({children: items}, 'item1-1-3'));
console.log(searchIt({children: items}, 'item1-1-3-1'));
console.log(searchIt({children: items}, 'item1-2-1'));
console.log(searchIt({children: items}, 'item1-1-3-2'));
console.log(searchIt({children: items}, 'item1-2-2'));
console.log(searchIt({children: items}, 'item3'));
You could take an recursive and iterative approach. On found, the path is returned from the most inner object to the outer call of the function.
function getPath(array, id) {
var result;
array.some((o, i) => {
var temp;
if (o.id === id) return result = `${i + 1}`;
if (temp = getPath(o.children || [], id)) return result = `${i + 1}-${temp}`;
});
return result;
}
const items = [{ id: 'item1', children: [{ id: 'item1-1', children: [{ id: 'item1-1-1' }, { id: 'item1-1-2' }, { id: 'item1-1-3', children: [{ id: 'item1-1-3-1'}] }] }, { id: 'item1-2', children: [{ id: 'item1-2-1' }] }] }, { id: 'item2' }];
console.log(getPath(items, 'item1')); // '1'
console.log(getPath(items, 'item1-2')); // '1-2'
console.log(getPath(items, 'item1-1-1')); // '1-1-1'
console.log(getPath(items, 'item1-1-2')); // '1-1-2'
console.log(getPath(items, 'item2')); // '2'
You can solve this problem using recursion. I have edited my code block and made it into a testable snippet. I had to fix an error in your data (missing comma or something don't remember).
const items = [
{ id: 'itemA',
children: [
{ id: 'item1-1',
children: [
{ id: 'item1-1-1' },
{ id: 'item1-1-2' },
{ id: 'item1-1-3', children: [ { id: 'item1-1-3-1'} ] },
]
},
{ id: 'item1-2', children: [ { id: 'item1-2-1' } ] },
],
},
{ id: 'item2' }
];
const getItemLevel = (targetKey, item, depth = 0) => {
if (item.id === targetKey) return depth;
let foundLevel = null;
if (item.children) {
item.children.forEach((child) => {
if (foundLevel) return;
foundLevel = getItemLevel(targetKey, child, depth +1);
})
}
return foundLevel;
}
console.log(getItemLevel('item1-1-1', { id:'root', children: items }));
console.log(getItemLevel('item2', { id:'root', children: items }));
console.log(getItemLevel('item1-1-3-1', { id:'root', children: items }));
console.log(getItemLevel('keydoesnotexist', { id:'root', children: items }));
a simple way:
const recursiveFind = (arr, id, res = {indexes: [], found: false}) => {
if (!Array.isArray(arr)) return res
const index = arr.findIndex(e => e.id === id)
if (index < 0) {
for (let i = 0; i < arr.length; i++) {
res.indexes.push(i+1)
const childIndexes = recursiveFind(arr[i].children, id, res)
if (childIndexes.found){
return childIndexes
}
}
}
else {
res.found = true
res.indexes.push(index+1)
}
return res
}
recursiveFind(items, 'item1-1-2').indexes.join('-')
If it's ok to use Lodash+Deepdash, then:
let path;
_.eachDeep(items,(val,key,parent,context)=>{
if(path) return false;
if(val.id=='item1-1-2'){
path=context.path;
return false;
}
},{tree:true,pathFormat:'array'});
console.log(_(path).without('children').map(v=>parseInt(v)+1).join('-'));
Here is a codepen for this

Get list of duplicate objects in an array of objects

I am trying to get duplicate objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' },
{ id: 11, name: 'someName3' },
{ id: 12, name: 'someName4' }
];
Duplicate objects should return like below:
duplicate = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' }
];
You can use Array#reduce to make a counter lookup table based on the id key, then use Array#filter to remove any items that appeared only once in the lookup table. Time complexity is O(n).
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const lookup = values.reduce((a, e) => {
a[e.id] = ++a[e.id] || 0;
return a;
}, {});
console.log(values.filter(e => lookup[e.id]));
Let's say you have:
arr = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
So, to get unique items:
unique = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) === i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]);
Then, result will be
unique = [
{ id:10, name: 'someName1' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
And, to get duplicate ids:
duplicateIds = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) !== i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]["id"])
List of IDs will be
duplicateIds = [10]
Thus, to get duplicates objects:
duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));
Now you have it:
duplicate = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' }
]
Thanks https://reactgo.com/removeduplicateobjects/
You haven't clarified whether two objects with different ids, but the same "name" count as a duplicate. I will assume those do not count as a duplicate; in other words, only objects with the same id will count as duplicate.
let ids = {};
let dups = [];
values.forEach((val)=> {
if (ids[val.id]) {
// we have already found this same id
dups.push(val)
} else {
ids[val.id] = true;
}
})
return dups;
With lodash you can solve this with filter and countBy for complexity of O(n):
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could do the same with ES6 like so:
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))
You can use an array to store unique elements and use filter on values to only return duplicates.
const unique = []
const duplicates = values.filter(o => {
if(unique.find(i => i.id === o.id && i.name === o.name)) {
return true
}
unique.push(o)
return false;
})
With lodash you can use _.groupBy() to group elements by their id. Than _.filter() out groups that have less than two members, and _.flatten() the results:
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const result = _.flow([
arr => _.groupBy(arr, 'id'), // group elements by id
g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
_.flatten // flatten the results to a single array
])(values);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
An alternative based in #ggorlen solution with new Map() as accumulator (for better performance) and without unary operator ++ (not advised by default in projects with ESLint).
const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];
const lookup = values.reduce((a, e) => {
a.set(e.id, (a.get(e.id) ?? 0) + 1);
return a;
}, new Map());
console.log(values.filter(e => lookup.get(e.id) > 1));
Try this
function checkDuplicateInObject(propertyName, inputArray) {
var seenDuplicate = false,
testObject = {};
inputArray.map(function(item) {
var itemPropertyName = item[propertyName];
if (itemPropertyName in testObject) {
testObject[itemPropertyName].duplicate = true;
item.duplicate = true;
seenDuplicate = true;
}
else {
testObject[itemPropertyName] = item;
delete item.duplicate;
}
});
return seenDuplicate;
}
referred from : http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/

How to get index of a 2d array of 2 similar structure ones with same value

Sorry the title may not present well.
I got two 2d arrays with similar structure.
array A:
arrayA[0]['account_name'] = 'a0';
arrayA[1]['account_name'] = 'a1';
arrayA[2]['account_name'] = 'a2';
And array B:
arrayB[0]['account_name'] = 'a1';
arrayB[1]['account_name'] = 'b0';
arrayB[2]['account_name'] = 'c0';
arrayB[3]['account_name'] = 'a0';
arrayB[4]['account_name'] = 'd3';
arrayB[5]['account_name'] = 'e8';
arrayB[6]['account_name'] = 'a3';
arrayB[7]['account_name'] = 'b4';
arrayB[8]['account_name'] = 'b1';
Now I know arrayA[0]['account_name'] equals to "a0", how can I search efficiently to check if it also exists in array B / know its position in array B? And I would like to loop for all values in array A.
const a = [
{ name: 'a0' },
{ name: 'a1' },
{ name: 'b2' }
];
const b = [
{ name: 'a0' },
{ name: 'a1' },
{ name: 'a2' },
{ name: 'b0' },
{ name: 'b1' },
{ name: 'b2' }
];
a.forEach((aa, i) => {
let found;
b.forEach((bb, j) => {
if(aa.name === bb.name) {
found = {
index: j,
value: aa.name
};
return true;
}
});
console.log(found);
});

Categories

Resources