มีฟังก์ชันใน lodash เพื่อแทนที่รายการที่ตรงกันหรือไม่


134

ฉันสงสัยว่ามีวิธีที่ง่ายกว่าในการแทนที่รายการในคอลเลกชัน JavaScript หรือไม่? (อาจซ้ำกันได้แต่ฉันไม่เข้าใจคำตอบที่นั่น :)

ฉันดูเอกสารของพวกเขา แต่ไม่พบอะไรเลย

รหัสของฉันคือ:

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
// Can following code be reduced to something like _.XX(arr, {id:1}, {id:1, name: "New Name"});
_.each(arr, function(a, idx){
  if(a.id === 1){
    arr[idx] = {id:1, name: "Person New Name"};
    return false;
  }
});

_.each(arr, function(a){
  document.write(a.name);
});

อัปเดต: วัตถุที่ฉันพยายามแทนที่มีคุณสมบัติมากมายเช่น

{id: 1, Prop1: ... , Prop2: ... และอื่น ๆ }

สารละลาย:

ขอบคุณdfsqแต่ฉันพบวิธีแก้ปัญหาที่เหมาะสมภายใน lodash ซึ่งดูเหมือนว่าจะทำงานได้ดีและค่อนข้างเรียบร้อยและฉันก็ใส่ไว้ใน mixin เช่นกันเนื่องจากฉันมีข้อกำหนดนี้ในหลาย ๆ ที่ JSBin

var update = function(arr, key, newval) {
  var match = _.find(arr, key);
  if(match)
    _.merge(match, newval);
  else
    arr.push(newval);    
};

_.mixin({ '$update': update });

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

_.$update(arr, {id:1}, {id:1, name: "New Val"});


document.write(JSON.stringify(arr));

โซลูชันที่เร็วขึ้น ตามที่ระบุไว้โดย @dfsq วิธีต่อไปนี้เร็วกว่า

var upsert = function (arr, key, newval) {
    var match = _.find(arr, key);
    if(match){
        var index = _.indexOf(arr, _.find(arr, key));
        arr.splice(index, 1, newval);
    } else {
        arr.push(newval);
    }
};

7
ฉันคิดว่าคุณสามารถใช้การจับคู่เป็นพารามิเตอร์ตัวที่สองกับ _.indexOf บน liine 4 ของ "Faster Solution" ของคุณได้เช่นกันไม่จำเป็นต้องคำนวณค่านั้นใหม่ที่นั่นซึ่งจะทำให้สิ่งต่างๆเร็วขึ้นเล็กน้อย
davertron

2
เร็วยิ่งขึ้น: ใช้_.findIndexสำหรับการแข่งขัน
Julian K

1
เพียงเพื่อขยายสิ่งที่ @JulianK และ @davertron กล่าวการใช้_.findIndexแทน_.findจะช่วยให้คุณทิ้งทั้งสอง_.findและ_.indexOf. คุณกำลังวนซ้ำอาร์เรย์ 3 ครั้งเมื่อสิ่งที่คุณต้องการคือ 1
Justin Morgan

คำตอบ:


191

ในกรณีของคุณสิ่งที่คุณต้องทำคือค้นหาวัตถุในอาร์เรย์และใช้Array.prototype.splice()วิธีการอ่านรายละเอียดเพิ่มเติมที่นี่ :

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

// Find item index using _.findIndex (thanks @AJ Richardson for comment)
var index = _.findIndex(arr, {id: 1});

// Replace item at index using native splice
arr.splice(index, 1, {id: 100, name: 'New object.'});

// "console.log" result
document.write(JSON.stringify( arr ));
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>


1
โซลูชันของคุณจะมีราคาสูงขึ้นในแง่ของประสิทธิภาพเนื่องจากindexOfจะเร็วมาก (จะใช้เบราว์เซอร์ดั้งเดิม Array.prototype.indexOf) แต่อย่างไรก็ตามดีใจที่คุณพบวิธีแก้ปัญหาที่เหมาะกับคุณ
dfsq

14
ทำไมไม่ใช้_.findIndex? _.indexOfแล้วคุณไม่จำเป็นต้องใช้
AJ Richardson

35

ดูเหมือนว่าวิธีที่ง่ายที่สุดคือการใช้ ES6 .mapหรือ lodash _.map:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

// lodash
var newArr = _.map(arr, function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

// ES6
var newArr = arr.map(function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

สิ่งนี้มีผลดีในการหลีกเลี่ยงการกลายพันธุ์ของอาร์เรย์เดิม


8
แต่คุณกำลังสร้างอาร์เรย์ใหม่ทุกครั้ง ... ควรทราบ
kboom

3
ทางเลือกเดียวที่จะไม่สร้างอาร์เรย์ใหม่คือการเปลี่ยนอาร์เรย์ที่มีอยู่ นอกจากนี้การสร้างอาร์เรย์ใหม่จะไม่มีผลกระทบในแง่ของประสิทธิภาพ โหวตจากฉัน
โนบิตะ

24

[ES6]รหัสนี้ใช้ได้สำหรับฉัน

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)

1. คุณกำลังสร้างอินสแตนซ์ใหม่ของอาร์เรย์ดังนั้นจึงไม่เป็นความจริงในการ "แทนที่" รายการ 2. คุณจะหายไปถ้าอาร์เรย์ไม่รวมถึงรายการที่มีเหมือนกันupdatedItem id
ชั่วร้าย

นี่เป็นวิธีแก้ปัญหาสำหรับ 'การอัปเดต' ไม่ใช่ 'การอัปเดต' (คำถามคือ "มีฟังก์ชันใน lodash เพื่อแทนที่รายการที่ตรงกัน") และใช่มันสร้างสำเนาของอาร์เรย์ดังนั้นอย่าใช้มันหากคุณต้องการทำงานกับ อาร์เรย์เดียวกัน (ฉันไม่ได้)
Shebik

21
function findAndReplace(arr, find, replace) {
  let i;
  for(i=0; i < arr.length && arr[i].id != find.id; i++) {}
  i < arr.length ? arr[i] = replace : arr.push(replace);
}

ตอนนี้เรามาทดสอบประสิทธิภาพสำหรับวิธีการทั้งหมด:


6
ถูกโหวตลงเพราะการมองโลกในแง่ลบกับผู้คน ("นี่เป็นเรื่องตลกไหม") ทำให้พวกเขาไม่เรียนรู้ ลองนึกภาพว่าถ้าฉันจบสิ่งนี้ด้วย "การเป็นคนฉลาดฉันคาดหวังว่าคุณจะไม่ขี้เกียจกับอารมณ์และคิดถึงสิ่งนั้น"
Aditya MP

5
ฉันไม่ต้องการทำร้ายใคร แต่ฉันสงสัยว่าวิธีแก้ปัญหาที่แย่ที่สุดกว่าแนวทางของผู้สร้างหัวข้อดั้งเดิมได้รับคะแนนโหวตมากมายขนาดนี้ได้อย่างไร กฎอะไรสำหรับคนที่ให้คะแนนนั้น? และฉันรู้สึกผิดหวังที่ผู้คนมักจะเชื่อถือคำตอบที่ได้รับการโหวตมากที่สุดและไม่มีความคิดเชิงวิพากษ์
ชั่วร้าย

1
@evilive คะแนนที่ถูกต้อง แต่ฉันไม่เห็นว่าสิ่งเหล่านี้ต้องการให้คุณเจอเหมือนกับว่าทุกคนที่ให้คำตอบ / โหวตก่อนหน้านี้เป็นคนงี่เง่า ส่วนที่เป็นข้อเท็จจริงของคำตอบนี้ดีมากส่วนที่เหลือมีความซับซ้อนที่เหนือกว่าแทบจะไม่มี นั่นไม่ได้ช่วยใคร คุณสามารถทำคะแนนได้อย่างง่ายดายโดยไม่ต้องตอบสนองทางอารมณ์มากเกินไป
ธ . 84no

1
โปรดทราบว่าโซลูชันของคุณและโซลูชันของ TC จะกรองตามรหัสเท่านั้น นั่นเป็นเหตุผลแรกที่ทำให้ทั้งสองวิ่งเร็วขึ้น อีกสองตัวช่วยให้คุณสามารถส่งผ่านส่วนใดก็ได้ของวัตถุที่คุณต้องการสำหรับการกรองซึ่งอาจเป็นที่ต้องการมากกว่าในฐานะฟังก์ชันอัพเปอร์
อร่าม

10

คุณยังสามารถใช้ findIndex และเลือกเพื่อให้ได้ผลลัพธ์เดียวกัน:

  var arr  = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
  var data = {id: 2, name: 'Person 2 (updated)'};
  var index = _.findIndex(arr, _.pick(data, 'id'));
  if( index !== -1) {
    arr.splice(index, 1, data);
  } else {
    arr.push(data);
  }

7

เมื่อเวลาผ่านไปคุณควรใช้แนวทางการทำงานที่มากขึ้นซึ่งคุณควรหลีกเลี่ยงการกลายพันธุ์ของข้อมูลและเขียนฟังก์ชันความรับผิดชอบเดียวที่มีขนาดเล็ก ด้วย ECMAScript 6 มาตรฐานคุณสามารถเพลิดเพลินไปกับกระบวนทัศน์การเขียนโปรแกรมการทำงานใน JavaScript กับที่ให้ไว้map, filterและreduceวิธีการ คุณไม่จำเป็นต้องมีเครื่องหมายขีดล่างหรือสิ่งอื่นใดเพื่อทำสิ่งพื้นฐานที่สุด

ด้านล่างฉันได้รวมวิธีแก้ปัญหาที่นำเสนอสำหรับปัญหานี้เพื่อแสดงให้เห็นว่าปัญหานี้สามารถแก้ไขได้อย่างไรโดยใช้คุณสมบัติภาษาต่างๆ:

การใช้แผนที่ ES6:

const replace = predicate => replacement => element =>
  predicate(element) ? replacement : element
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = arr.map(replace (predicate) (replacement))
console.log(result)


เวอร์ชันเรียกซ้ำ - เทียบเท่ากับการทำแผนที่:

ต้องdestructuringและอาร์เรย์การแพร่กระจาย

const replace = predicate => replacement =>
{
  const traverse = ([head, ...tail]) =>
    head
    ? [predicate(head) ? replacement : head, ...tail]
    : []
  return traverse
}
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = replace (predicate) (replacement) (arr)
console.log(result)


เมื่อลำดับสุดท้ายของอาร์เรย์ไม่สำคัญคุณสามารถใช้objectเป็นโครงสร้างข้อมูลHashMapได้ มีประโยชน์มากหากคุณได้ใส่กุญแจคอลเลกชันเป็นobject- มิฉะนั้นคุณต้องเปลี่ยนการแสดงก่อน

ต้องใช้วัตถุที่เหลือแพร่กระจาย , ชื่อคุณสมบัติคำนวณและ Object.entries

const replace = key => ({id, ...values}) => hashMap =>
({
  ...hashMap,       //original HashMap
  [key]: undefined, //delete the replaced value
  [id]: values      //assign replacement
})

// HashMap <-> array conversion
const toHashMapById = array =>
  array.reduce(
    (acc, { id, ...values }) => 
    ({ ...acc, [id]: values })
  , {})
  
const toArrayById = hashMap =>
  Object.entries(hashMap)
  .filter( // filter out undefined values
    ([_, value]) => value 
  ) 
  .map(
    ([id, values]) => ({ id, ...values })
  )

const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const replaceKey = 1
const replacement = { id: 100, name: 'New object.' }

// Create a HashMap from the array, treating id properties as keys
const hashMap = toHashMapById(arr)
console.log(hashMap)

// Result of replacement - notice an undefined value for replaced key
const resultHashMap = replace (replaceKey) (replacement) (hashMap)
console.log(resultHashMap)

// Final result of conversion from the HashMap to an array
const result = toArrayById (resultHashMap)
console.log(result)


5

หากคุณเพียงแค่พยายามที่จะแทนที่หนึ่งคุณสมบัติ lodash _.findและ_.setควรจะเพียงพอที่:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

_.set(_.find(arr, {id: 1}), 'name', 'New Person');

1

หากจุดแทรกของวัตถุใหม่ไม่จำเป็นต้องตรงกับดัชนีของออบเจ็กต์ก่อนหน้าวิธีที่ง่ายที่สุดในการทำเช่นนี้กับ lodash คือการใช้_.rejectแล้วผลักค่าใหม่ในอาร์เรย์:

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }
];

arr = _.reject(arr, { id: 1 });
arr.push({ id: 1, name: "New Val" });

// result will be: [{ id: 2, name: "Person 2" }, { id: 1, name: "New Val" }]

หากคุณมีหลายค่าที่ต้องการแทนที่ในรอบเดียวคุณสามารถดำเนินการดังต่อไปนี้ (เขียนในรูปแบบที่ไม่ใช่ ES6):

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }, 
  { id: 3, name: "Person 3" }
];

idsToReplace = [2, 3];
arr = _.reject(arr, function(o) { return idsToReplace.indexOf(o.id) > -1; });
arr.push({ id: 3, name: "New Person 3" });
arr.push({ id: 2, name: "New Person 2" });


// result will be: [{ id: 1, name: "Person 1" }, { id: 3, name: "New Person 3" }, { id: 2, name: "New Person 2" }]

วิธีนี้เปลี่ยนการเรียงลำดับอาร์เรย์
sospedra

1

ด้วยการใช้ lodash union ด้วยฟังก์ชั่นคุณสามารถเปลี่ยนวัตถุได้อย่างง่ายดาย เอกสารระบุว่าหากมีการจับคู่จะใช้อาร์เรย์แรก ห่ออ็อบเจ็กต์ที่อัปเดตของคุณใน [] (อาร์เรย์) และวางเป็นอาร์เรย์แรกของฟังก์ชันยูเนี่ยน เพียงระบุตรรกะการจับคู่ของคุณและหากพบจะแทนที่และหากไม่มีจะเพิ่ม

ตัวอย่าง:

let contacts = [
     {type: 'email', desc: 'work', primary: true, value: 'email prim'}, 
     {type: 'phone', desc: 'cell', primary: true, value:'phone prim'},
     {type: 'phone', desc: 'cell', primary: false,value:'phone secondary'},
     {type: 'email', desc: 'cell', primary: false,value:'email secondary'}
]

// Update contacts because found a match
_.unionWith([{type: 'email', desc: 'work', primary: true, value: 'email updated'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)

// Add to contacts - no match found
_.unionWith([{type: 'fax', desc: 'work', primary: true, value: 'fax added'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)



0

หากคุณกำลังมองหาวิธีเปลี่ยนคอลเลกชันอย่างไม่เปลี่ยนแปลง (เหมือนตอนที่ฉันพบคำถามของคุณ) คุณอาจดูตัวช่วยไม่เปลี่ยนรูปซึ่งเป็นไลบรารีที่แยกออกจากเครื่องมือตอบสนองดั้งเดิม ในกรณีของคุณคุณจะทำสิ่งที่คุณกล่าวถึงผ่านทางต่อไปนี้:

var update = require('immutability-helper')
var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}]
var newArray = update(arr, { 0: { name: { $set: 'New Name' } } })
//=> [{id: 1, name: "New Name"}, {id:2, name:"Person 2"}]

0

คุณสามารถทำได้โดยไม่ต้องใช้ lodash

let arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];
let newObj = {id: 1, name: "new Person"}

/*Add new prototype function on Array class*/
Array.prototype._replaceObj = function(newObj, key) {
  return this.map(obj => (obj[key] === newObj[key] ? newObj : obj));
};

/*return [{id: 1, name: "new Person"}, {id: 2, name: "Person 2"}]*/
arr._replaceObj(newObj, "id") 

0

ไม่เปลี่ยนรูปเหมาะสำหรับReactJS:

สมมติ:

cosnt arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

รายการที่อัปเดตเป็นรายการที่สองและเปลี่ยนชื่อเป็นSpecial Person:

const updatedItem = {id:2, name:"Special Person"};

คำแนะนำ : lodashมีเครื่องมือที่มีประโยชน์ แต่ตอนนี้เรามีบางส่วนใน Ecmascript6 + ดังนั้นฉันจึงใช้mapฟังก์ชันที่มีอยู่ในทั้งสองlodashและecmascript6+:

const newArr = arr.map(item => item.id === 2 ? updatedItem : item);

0

เจอสิ่งนี้เช่นกันและทำอย่างนั้น

const persons = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
const updatedPerson = {id: 1, name: "new Person Name"}
const updatedPersons = persons.map(person => (
  person.id === updated.id
    ? updatedPerson
    : person
))

หากต้องการเราสามารถสรุปได้

const replaceWhere = (list, predicate, replacement) => {
  return list.map(item => predicate(item) ? replacement : item)
}

replaceWhere(persons, person => person.id === updatedPerson.id, updatedPerson)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.