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)
Related
This question already has answers here:
Group array items using object
(19 answers)
Closed last year.
I have an array of objects and I would like to group the objects which have same name and make an array containing the other values which differs. How can I achieve that?
const arr = [
{
name: 'A',
color: 'blue',
},
{
name: 'A',
color: 'purple',
},
{
name: 'B',
color: 'Yellow',
},
{
name: 'B',
color: 'Green',
},
];
What I would like to get:
const result = [
{
name: 'A',
color: ['blue', 'purple'],
},
{
name: 'B',
color: ['Yellow', 'Green'],
},
];
This looks like something reduce() should be used for.
Use find() to find in the existing array element based on some condition.
If element exists, push into colors property of the element.
Else push into the array a new object.
const arr = [
{
name: 'A',
color: 'blue',
},
{
name: 'A',
color: 'purple',
},
{
name: 'B',
color: 'Yellow',
},
{
name: 'B',
color: 'Green',
},
];
let ans = arr.reduce((agg,curr) => {
let found = agg.find((x) => x.name === curr.name);
if(found){
found.colors.push(curr.color);
}
else{
agg.push({
name : curr.name,
colors : [curr.color]
});
}
return agg;
},[]);
console.log(ans);
const found = acc.find(item => item.name === curr.name);
if (found) {
found.color.push(curr.color);
} else {
acc.push({
name: curr.name,
color: [curr.color],
});
}
return acc;
}
, []);
Here is one way to do it:
const arrNames = Array.from(new Set(arr.map((x) => x.name))); // make an array of unique names
const result = arrNames
.map((x) => arr.filter((y) => y.name === x)) // filter by name
.map((x, i) => ({ name: arrNames[i], color: x.map((y) => y.color) })); // make new objects
Create set of props then loop over possible names and filter their values O(n^2)
const set = new Set(arr.map((obj) => obj.name));
const res = [];
for(const name of set.keys()) {
const colors = arr.filter((obj) => obj.name === name).map((obj) => obj.color);
res.push({name, colors});
}
Or create a dictionary whose keys will be name-s, and values - array O(n)
const mp = new Map();
for (const obj of arr) {
if (mp.has(obj.name)) {
mp.get(obj.name).push(obj.color);
} else {
mp.set(obj.name, [obj.color]);
}
}
const result = [];
for (const [name, color] of mp.entries()) {
result.push({name, color});
}
let results = [];
const arr = [
{
name: "A",
color: "blue",
},
{
name: "A",
color: "purple",
},
{
name: "B",
color: "Yellow",
},
{
name: "B",
color: "Green",
},
];
const names = arr.map((element) => element.name);
const uniqueNames = [...new Set(names)];
uniqueNames.forEach((element) => {
let temp = {};
temp.name = element;
temp.color = [];
arr.forEach((element2) => {
if (element === element2.name) {
temp.color.push(element2.color);
}
});
results.push(temp);
});
console.log("results", results);
I have a data inside of my array, which looks like this.
const arr = [
{
devSth: {
h1: [1],
h2: [1],
}
},
];
I have a function, which should modify this array. It looks like this:
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
Then I am using my function:
const newArr = modifyArr(arr);
console.log(newArr);
Expected output:
[{
devSth: {
h1: [{name: 1, val: 'lalala'}],
h2: [{name: 1, val: 'lalala'}]
}
}]
Actual output:
[{
devSth: {
h1: [1],
h2: [1]
}
}]
Would appreciate any help.
you are mapping the values, but not setting them (line 5) :
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
this is untested.
You dont assign the result of the .map operation back to the original keys h1 and h2. So it doesn't modify those.
Just change
el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
to
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
const arr = [{
devSth: {
h1: [1],
h2: [1],
}
}, ];
const modifyArr = (data) => {
data.forEach(el => {
let keys = el.devSth && Object.keys(el.devSth);
keys.forEach(key => {
el.devSth[key] = el.devSth[key].map(el => {
return {
name: el,
val: 'lalala'
}
})
})
})
return data;
}
const newArr = modifyArr(arr);
console.log(newArr);
An alternative way of doing it:
const arr = [{
devSth: {h1: [1], h2: [1]}
}];
let obj = arr[0].devSth, number;
for (const prop in obj){
number = obj[prop][0];
obj[prop] = [{name: number, val: 'lalala'}];
}
console.log([{devSth: obj}])
const r = ['a', 'b', 'c', 'l', 'p'];
const arr = [{
name: "ss3",
id: 'c'
}, {
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
}, {
name: "ss1",
id: 'a'
}]
var newArray =arr.map((i)=>{
let e = r[i];
if(i.id===e){
return i
}
})
console.log(newArray)
Expected output
const arr = [{
name: "ss1",
id: 'a'
}, {
name: "ss2",
id: 'b'
}, {
name: "ss3",
id: 'c'
}, {
name: "ss4",
id: 'p'
}
]
Given two arrays r and arr, I wish to sort arr with respect to r, i.e. in alphabetical order by id.
https://jsbin.com/yitijiboso/edit?html,js,output
I think this might be a concise (although not very performant) way to achieve the desired output:
const arr1 = ['a', 'b', 'c', 'l', 'p'];
const arr2 = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr2.sort((a, b) => arr1.indexOf(a.id) - arr1.indexOf(b.id));
console.log(arr2);
Easy:
make a map from main 'arr' keyBy 'id' https://www.npmjs.com/package/lodash.keyby
loop across 'r', if key exist in new map, get value and push to new array
const arrMap = _.keyBy(arr, 'id');
let newR = [];
r.forEach( key => {
if ( arrMap[key] ) {
newR.push( arrMap[key] );
}
} );
console.log( 'new array', newR );
Taking a clue from #Petr Broz, here's my suggestion:
const r = ['a', 'b', 'c', 'l', 'p'];
const arr = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr.sort((a, b) => r.indexOf(a.id) > r.indexOf(b.id));
console.log(arr);
Main difference is that this code utilizes the arrays as named by the OP and uses a greater than comparison operator. However, if you just want to have the array arr sorted in alphabetical order you don't need to compare it with array r:
const arr = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr.sort(function(a, b)
{
if (a.id > b.id) {
return 1;
}
else
if (a.id < b.id) {
return -1;
}
else
{
return 0;
}
});
console.log(arr);
Note, in this example the return values are numeric instead of boolean values which would be helpful if the array to be sorted were to have duplicate values.
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;
}
Assuming we have the following Object, what would be the best way to iterate it up to it's end in order to get the name property for each Object?
Please notice, that the size of the Object may vary and the browsing should be done in this order: a, b, a1, a2, b1, a21, b11, b12 ...
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b'
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
You could use a breadth-first search. It is an algorithm which is iterating every level of the tree first and then the next level.
This implementation works with a queue of nodes, that means, to call the function breadthFirst, the object/single node must be wrapped in an array.
function breadthFirst(queue) {
var newQueue = [];
queue.forEach(function (node) {
('name' in node) && console.log(node.name);
Object.keys(node).forEach(function (k) {
node[k] && typeof node[k] === 'object' && newQueue.push(node[k]);
});
});
newQueue.length && breadthFirst(newQueue);
}
var object = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } };
breadthFirst([object]); // a b a1 a2 b1 a21 b11 b12
.as-console-wrapper { max-height: 100% !important; top: 0; }
What you are looking for is a breadth-first solution which Nina has rightly mentioned. Here is my implementation of it. In this solution, you can store the result in the array and then do console.log later.
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b',
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
var ans = [];
var q = [];
q.push(obj);
function getAllKeys() {
if (q.length == 0) {
return;
}
var obj = q.shift();
var keys = Object.keys(obj);
ans = ans.concat(keys);
var index = ans.indexOf('name');
if (index != -1) {
ans.splice(index, 1);
}
for (var i = 0; i < keys.length; i++) {
if (typeof obj[keys[i]] == 'object') {
q.push(obj[keys[i]]);
}
}
getAllKeys();
}
getAllKeys();
console.log(ans);
You need a recursive thing here. You are free to change the console.log to push somewhere or whatever...
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b',
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
var looping = function(obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if(typeof obj[keys[i]] === 'string') console.log(obj[keys[i]]);
else looping(obj[keys[i]]);
}
}
looping(obj);
Here's a simple recursive function to get all the name properties in breadth-first order. I'm using a helper, pairs, that makes it easier to process the key-value pairs provided by each object. From there, it's a simple case analysis for how the recursive function should respond:
base case – return the accumulator
key is name, append value to the accumulator
value is an object, add pairs of value to the list of pairs to process
default case - do nothing and process the next pair
This answer differs from others in that there is no side effect from running it. Instead of hard coding some behavior in the loop function itself, loop returns an array of name property values that you can then do with whatever you wish.
const pairs = o =>
Object.keys(o).map(k => ({key: k, value: o[k]}))
const loop = o => {
const aux = (acc, [x,...xs]) => {
if (x === undefined)
return acc
else if (x.key === 'name')
return aux([...acc, x.value], xs)
else if (Object(x.value) === x.value)
return aux(acc, xs.concat(pairs(x.value)))
else
return aux(acc, xs)
}
return aux([], pairs(o))
}
const obj = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } }
console.log(loop(obj))
// [ 'a', 'b', 'a1', 'a2', 'b1', 'a21', 'b11', 'b12' ]
Alternatively, you could implement loop using a generator such that you could act on the values while iterating. Let me know if this interests you and I'll do a write up.
Edit
Original answer processed the object in the incorrect order. The above code now answers the question properly ^_^
You are looking for breadth-first traversal:
// Breadh-first object traversal:
function traverse(...objs) {
for (let obj of objs) {
let next = Object.values(obj).filter(val => val && typeof val === 'object');
objs.push(...next);
}
return objs;
}
// Example:
let obj = {
a:{name:"a",a1:{name:"a1"},a2:{name:"a2",a21:{name:"a21"}}},
b:{name:"b",b1:{name:"b1",b11:{name:"b11"},b12:{name:"b12"}}}
};
for (let child of traverse(obj)) {
if (child.name) console.log(child.name);
}